diff --git a/Cargo.lock b/Cargo.lock index 95edba8b..2f638520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4061,6 +4061,7 @@ dependencies = [ "serde", "serde_json", "specta", + "tempfile", "tracing", "which", ] @@ -4248,6 +4249,7 @@ dependencies = [ "ide-core", "libc", "parking_lot", + "portable-pty", "serde", "serde_json", "specta", @@ -4266,6 +4268,7 @@ dependencies = [ "ide-core", "ide-language", "ide-run", + "ide-vfs", "serde", "serde_json", "specta", @@ -4285,6 +4288,7 @@ dependencies = [ "grep-regex", "grep-searcher", "ide-core", + "ide-vfs", "ignore", "insta", "memchr", @@ -4294,6 +4298,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "ide-spell" +version = "0.1.13" +dependencies = [ + "ide-core", + "parking_lot", + "serde", + "specta", + "spellbook", + "tracing", +] + [[package]] name = "ide-syntax" version = "0.1.13" @@ -4389,6 +4405,7 @@ dependencies = [ "tempfile", "tracing", "trash", + "zip 2.4.2", ] [[package]] @@ -4426,6 +4443,7 @@ dependencies = [ "toml 0.9.12+spec-1.1.0", "toml_edit 0.25.17+spec-1.1.0", "tracing", + "zip 2.4.2", ] [[package]] @@ -5854,6 +5872,7 @@ dependencies = [ "ide-run", "ide-sdk", "ide-search", + "ide-spell", "ide-syntax", "ide-terminal", "ide-text", @@ -7719,6 +7738,16 @@ dependencies = [ "specta", ] +[[package]] +name = "spellbook" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d204abcbdf8e88729306a8d0ca01a79d8a49969fe1f84696909d6dbc4321c1a" +dependencies = [ + "foldhash", + "hashbrown 0.17.1", +] + [[package]] name = "spin" version = "0.9.9" diff --git a/Cargo.toml b/Cargo.toml index f35896ba..b86fffa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ ide-profiler = { path = "crates/ide-profiler" } ide-http = { path = "crates/ide-http" } ide-db = { path = "crates/ide-db" } ide-web = { path = "crates/ide-web" } +ide-spell = { path = "crates/ide-spell" } # serialization & IPC typing serde = { version = "1", features = ["derive"] } diff --git a/THIRD_PARTY_LICENSES.md b/THIRD_PARTY_LICENSES.md index 46787cba..7d02d1e7 100644 --- a/THIRD_PARTY_LICENSES.md +++ b/THIRD_PARTY_LICENSES.md @@ -79,6 +79,15 @@ user's app-data directory only when the user asks; none of them ships with OpenI unmodified, and its source is available from the repository above. - **Eclipse JDT Language Server** (jdtls): EPL-2.0, from download.eclipse.org. +## en_US Hunspell dictionary + +`crates/ide-spell/src/dict/en_US.{aff,dic}` (the spell checker's bundled dictionary) is the +SCOWL-derived en_US Hunspell dictionary (version 2020.12.07, from the `dictionary-en` npm +package 4.0.0), copied unmodified. Copyright Kevin Atkinson and the SCOWL / Hunspell +dictionary contributors; MIT- and BSD-style licences, full text in +`crates/ide-spell/src/dict/LICENSE-en_US`. It is checked with the +[spellbook](https://github.com/helix-editor/spellbook) crate (MPL-2.0). + ## nvim-treesitter queries `crates/ide-syntax/queries//locals.scm` are derived from the `locals.scm` diff --git a/crates/ide-build/src/compile/mod.rs b/crates/ide-build/src/compile/mod.rs index 8d62d8e7..2d4d0b92 100644 --- a/crates/ide-build/src/compile/mod.rs +++ b/crates/ide-build/src/compile/mod.rs @@ -584,6 +584,7 @@ pub fn diagnostics(messages: &[BuildMessage]) -> BTreeMap Option { None } +/// Configurations whose dependencies are libraries (`testFixturesImplementation` ends in one). +const DEPENDENCY_CONFIGURATIONS: &[&str] = &[ + "implementation", + "api", + "compileOnly", + "runtimeOnly", + "compileOnlyApi", + "annotationProcessor", + "kapt", + "ksp", + "compile", + "runtime", + "testCompile", + "testRuntime", + "developmentOnly", +]; + +fn is_dependency_configuration(name: &str) -> bool { + DEPENDENCY_CONFIGURATIONS.iter().any(|c| { + name == *c || { + let mut cap = c.chars(); + let first = cap.next().map(|f| f.to_ascii_uppercase()).into_iter(); + let capitalised: String = first.chain(cap).collect(); + name.len() > c.len() && name.ends_with(&capitalised) + } + }) +} + +/// `(group, artifact, version)` of the external dependencies a build script declares: +/// `implementation 'g:a:v'`, `api("g:a:v:classifier@jar")`, and the map notation +/// `implementation group: 'g', name: 'a', version: 'v'` (Groovy) / `group = "g", …` (Kotlin). +/// Versions with interpolations, ranges and catalog aliases (`libs.x`) are skipped. +pub fn script_dependencies(text: &str) -> Vec<(String, String, String)> { + let text = strip_comments(text); + let mut out = Vec::new(); + for line in text.lines().flat_map(|l| l.split(['{', ';'])) { + let line = line.trim_start(); + let name: String = line.chars().take_while(|c| c.is_ascii_alphanumeric() || *c == '_').collect(); + if name.is_empty() || !is_dependency_configuration(&name) { + continue; + } + let rest = &line[name.len()..]; + let lits = literals(rest); + let map_value = |key: &str| -> Option { + let i = rest.find(key)?; + let after = rest[i + key.len()..].trim_start().strip_prefix([':', '='])?; + literals(after).into_iter().next() + }; + let coordinate = match (map_value("group"), map_value("name"), map_value("version")) { + (Some(g), Some(a), Some(v)) => Some((g, a, v)), + _ => lits.first().and_then(|l| { + let l = l.split('@').next().unwrap_or(l); + let mut parts = l.split(':'); + Some((parts.next()?.to_owned(), parts.next()?.to_owned(), parts.next()?.to_owned())) + }), + }; + if let Some((g, a, v)) = coordinate { + let plain = |s: &str| { + !s.is_empty() && s.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '-' | '_' | '+')) + }; + if plain(&g) && plain(&a) && plain(&v) && !v.contains('+') { + out.push((g, a, v)); + } + } + } + out +} + /// Every settings and build script of the model (for change detection). pub fn build_files(root: &Path, model: &GradleModel) -> Vec { let dir = root.join(&model.dir); @@ -333,6 +401,38 @@ pub fn parse_tasks(output: &str) -> Vec<(String, GradleTask)> { #[cfg(test)] mod tests { + #[test] + fn script_dependencies_read_string_and_map_notations() { + let script = r#" +plugins { id 'java' } +dependencies { + implementation 'com.google.guava:guava:33.0.0-jre' + api("org.slf4j:slf4j-api:2.0.9") + testImplementation "junit:junit:4.13.2@jar" + // implementation 'commented:out:1.0' + compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.30' + runtimeOnly(group = "com.h2database", name = "h2", version = "2.2.224") + implementation "org.x:dyn:${ver}" + implementation 'org.x:range:1.+' + implementation libs.okhttp + implementation project(':core') + classpath 'not:a:library' +} +"#; + let deps = script_dependencies(script); + let names: Vec = deps.iter().map(|(g, a, v)| format!("{g}:{a}:{v}")).collect(); + assert_eq!( + names, + [ + "com.google.guava:guava:33.0.0-jre", + "org.slf4j:slf4j-api:2.0.9", + "junit:junit:4.13.2", + "org.projectlombok:lombok:1.18.30", + "com.h2database:h2:2.2.224", + ] + ); + } + use super::*; #[test] diff --git a/crates/ide-build/src/lib.rs b/crates/ide-build/src/lib.rs index 726aa7b7..db686b0f 100644 --- a/crates/ide-build/src/lib.rs +++ b/crates/ide-build/src/lib.rs @@ -50,6 +50,56 @@ pub fn load(root: &Path, cache_dir: &Path, maven_repo: Option) -> Build BuildModel { maven, gradle } } +/// A library the project depends on, as `groupId:artifactId:version`. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DependencyCoordinate { + /// Gradle builds resolve into the Gradle cache, Maven ones into the local repository. + pub gradle: bool, + pub group_id: String, + pub artifact_id: String, + pub version: String, +} + +/// The external libraries of `model`, sorted and deduplicated: Maven dependencies +/// (with the transitive ones the last sync read), except the build's own modules, +/// `pom` imports and `system` scope; dependencies declared in the Gradle build scripts. +pub fn dependency_coordinates(root: &Path, model: &BuildModel) -> Vec { + let mut out = Vec::new(); + if let Some(m) = &model.maven { + let modules: std::collections::HashSet = m.projects.iter().map(MavenProject::key).collect(); + let mut stack: Vec<&MavenDependency> = m.projects.iter().flat_map(|p| &p.dependencies).collect(); + while let Some(d) = stack.pop() { + stack.extend(&d.children); + let Some(version) = d.version.as_deref().filter(|v| !v.is_empty() && !v.contains("${")) else { + continue; + }; + if d.kind == "pom" || matches!(d.scope.as_str(), "import" | "system") { + continue; + } + if modules.contains(&format!("{}:{}", d.group_id, d.artifact_id)) { + continue; + } + out.push(DependencyCoordinate { + gradle: false, + group_id: d.group_id.clone(), + artifact_id: d.artifact_id.clone(), + version: version.to_owned(), + }); + } + } + if let Some(g) = &model.gradle { + for script in g.projects.iter().filter_map(|p| p.build_file.as_ref()) { + let Ok(text) = std::fs::read_to_string(root.join(script)) else { continue }; + out.extend(gradle::script_dependencies(&text).into_iter().map(|(group_id, artifact_id, version)| { + DependencyCoordinate { gradle: true, group_id, artifact_id, version } + })); + } + } + out.sort(); + out.dedup(); + out +} + /// Whether a created / changed / removed project-relative path can change the build model. pub fn affects_build(rel: &Path) -> bool { let Some(name) = rel.file_name().and_then(|n| n.to_str()) else { return false }; @@ -78,4 +128,43 @@ mod tests { assert!(affects_build(Path::new("a/pom.xml")) && affects_build(Path::new("settings.gradle.kts"))); assert!(!affects_build(Path::new("src/Main.java"))); } + + #[test] + fn dependency_coordinates_flatten_maven_trees_and_read_gradle_scripts() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + std::fs::write(root.join("build.gradle"), "dependencies { implementation 'org.a:x:1.0' }\n").unwrap(); + let dep = |g: &str, a: &str, v: Option<&str>, children: Vec| MavenDependency { + group_id: g.into(), + artifact_id: a.into(), + version: v.map(Into::into), + kind: "jar".into(), + scope: "compile".into(), + children, + ..Default::default() + }; + let maven = MavenModel { + projects: vec![ + MavenProject { + group_id: "me".into(), + artifact_id: "app".into(), + dependencies: vec![ + dep("me", "core", Some("1"), vec![]), + dep("org.b", "y", Some("2"), vec![dep("org.c", "z", Some("3"), vec![])]), + dep("org.d", "unresolved", Some("${v}"), vec![]), + ], + ..Default::default() + }, + MavenProject { group_id: "me".into(), artifact_id: "core".into(), ..Default::default() }, + ], + ..Default::default() + }; + let gradle = gradle::load(root); + let model = BuildModel { maven: Some(maven), gradle }; + let names: Vec = dependency_coordinates(root, &model) + .into_iter() + .map(|c| format!("{}{}:{}:{}", if c.gradle { "G " } else { "M " }, c.group_id, c.artifact_id, c.version)) + .collect(); + assert_eq!(names, ["M org.b:y:2", "M org.c:z:3", "G org.a:x:1.0"]); + } } diff --git a/crates/ide-core/src/editor_settings.rs b/crates/ide-core/src/editor_settings.rs index 8bf44084..68b674e6 100644 --- a/crates/ide-core/src/editor_settings.rs +++ b/crates/ide-core/src/editor_settings.rs @@ -117,6 +117,12 @@ pub struct CompletionSettings { pub auto_popup: bool, /// Insert the selected suggestion by pressing space, dot, or other context-dependent keys. pub insert_on_special_keys: bool, + /// Insert a sole suggestion of explicit (⌃Space) completion right away. + pub auto_insert_single: bool, + /// Sort suggestions by name instead of relevance. + pub sort_alphabetically: bool, + /// Add `()` after function and method suggestions. + pub insert_parentheses: bool, pub show_documentation: bool, pub documentation_delay: u32, pub parameter_info: bool, @@ -130,6 +136,9 @@ impl Default for CompletionSettings { case_sensitivity: "first".into(), auto_popup: true, insert_on_special_keys: false, + auto_insert_single: true, + sort_alphabetically: false, + insert_parentheses: true, show_documentation: false, documentation_delay: 500, parameter_info: true, @@ -166,13 +175,22 @@ impl Default for FoldingSettings { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] #[serde(default, rename_all = "camelCase")] pub struct EditorTabsSettings { - /// `top` | `none`. + /// `top` | `bottom` | `none`. pub placement: String, pub multiple_rows: bool, + /// One row that doesn't fit: `scroll` the tabs panel | `squeeze` the tabs. + pub one_row_overflow: String, + pub pinned_separate_row: bool, + pub show_file_icon: bool, pub show_extension: bool, + pub show_directory_for_non_unique: bool, /// Mark modified tabs with an asterisk. pub mark_modified: bool, + pub show_full_path_on_hover: bool, pub show_close_button: bool, + /// `right` | `left` (with `show_close_button`). + pub close_button_position: String, + pub sort_alphabetically: bool, pub preview_tab: bool, pub open_at_end: bool, pub tab_limit: u32, @@ -187,9 +205,16 @@ impl Default for EditorTabsSettings { Self { placement: "top".into(), multiple_rows: false, + one_row_overflow: "scroll".into(), + pinned_separate_row: false, + show_file_icon: true, show_extension: true, + show_directory_for_non_unique: true, mark_modified: false, + show_full_path_on_hover: true, show_close_button: true, + close_button_position: "right".into(), + sort_alphabetically: false, preview_tab: true, open_at_end: false, tab_limit: 30, @@ -275,11 +300,38 @@ pub struct InlayHintsSettings { pub disabled_languages: Vec, /// Code vision: usages above declarations. pub code_vision_usages: bool, + /// Code vision: last author of a declaration (VCS blame) beside its usages. + pub code_vision_author: bool, } impl Default for InlayHintsSettings { fn default() -> Self { - Self { types: true, parameters: true, other: true, disabled_languages: Vec::new(), code_vision_usages: true } + Self { + types: true, + parameters: true, + other: true, + disabled_languages: Vec::new(), + code_vision_usages: true, + code_vision_author: true, + } + } +} + +/// Editor › General › Inline Completion: ghost text from language servers that +/// support `textDocument/inlineCompletion` (LSP 3.18). +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] +#[serde(default, rename_all = "camelCase")] +pub struct InlineCompletionSettings { + pub enabled: bool, + /// Ask only once typing has paused for `pause_ms`, instead of right after each change. + pub only_on_pause: bool, + pub pause_ms: u32, + pub disabled_languages: Vec, +} + +impl Default for InlineCompletionSettings { + fn default() -> Self { + Self { enabled: true, only_on_pause: false, pause_ms: 500, disabled_languages: Vec::new() } } } @@ -335,6 +387,14 @@ pub struct CodeStyleSettings { /// Enable EditorConfig support. pub editorconfig: bool, pub languages: BTreeMap, + /// Formatter › Do not format: globs (`*.min.js`, `generated/**`) Reformat Code skips. + pub do_not_format: Vec, + /// Formatter › Turn formatter on/off with markers in code comments. + pub formatter_tags_enabled: bool, + pub formatter_off_tag: String, + pub formatter_on_tag: String, + /// The markers are regular expressions. + pub formatter_tags_regexp: bool, } impl Default for CodeStyleSettings { @@ -346,6 +406,11 @@ impl Default for CodeStyleSettings { detect_indents: true, editorconfig: true, languages: BTreeMap::new(), + do_not_format: Vec::new(), + formatter_tags_enabled: false, + formatter_off_tag: "@formatter:off".into(), + formatter_on_tag: "@formatter:on".into(), + formatter_tags_regexp: false, } } } diff --git a/crates/ide-core/src/event.rs b/crates/ide-core/src/event.rs index 1db2bd6c..b9859ddc 100644 --- a/crates/ide-core/src/event.rs +++ b/crates/ide-core/src/event.rs @@ -32,6 +32,11 @@ pub enum IdeEvent { doc: DocId, dirty: bool, }, + /// An open document's language changed (Settings › File Types, Override File Type). + DocLanguage { + doc: DocId, + language: crate::LanguageId, + }, /// Full replacement of diagnostics for one file from one producer. Diagnostics { path: crate::IpcPath, diff --git a/crates/ide-core/src/inspection_settings.rs b/crates/ide-core/src/inspection_settings.rs new file mode 100644 index 00000000..7b28189b --- /dev/null +++ b/crates/ide-core/src/inspection_settings.rs @@ -0,0 +1,105 @@ +//! Settings › Editor › Inspections: inspection profiles. `Default` and the +//! user's IDE-level copies live in the IDE (global) settings, `Project Default` +//! and project-level copies in the project's; the project picks which one is in +//! effect. The UI applies the profile to diagnostics. Also Editor › Natural +//! Languages › Spelling (the Typo inspection's dictionaries). + +use serde::{Deserialize, Serialize}; +use specta::Type; +use std::collections::BTreeMap; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] +#[serde(default, rename_all = "camelCase")] +pub struct InspectionSettings { + /// Profile in effect for the project: `default`, `project`, or a copy + /// as `ide:` / `project:`. + pub profile: String, + /// The `Default` profile (IDE level): inspection id → override. + pub default: BTreeMap, + /// The `Project Default` profile (stored with the project). + pub project: BTreeMap, + /// Profiles copied at IDE level (global settings), by key. + pub ide_profiles: BTreeMap, + /// Profiles copied at project level (project settings), by key. + pub project_profiles: BTreeMap, +} + +impl Default for InspectionSettings { + fn default() -> Self { + Self { + profile: "default".into(), + default: BTreeMap::new(), + project: BTreeMap::new(), + ide_profiles: BTreeMap::new(), + project_profiles: BTreeMap::new(), + } + } +} + +/// A named profile made with Copy. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(default, rename_all = "camelCase")] +pub struct CustomInspectionProfile { + pub name: String, + pub overrides: BTreeMap, +} + +/// Editor › Natural Languages › Spelling. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(default, rename_all = "camelCase")] +pub struct SpellingSettings { + /// Application-level dictionary: accepted words (IDE settings). + pub ide_words: Vec, + /// Project-level dictionary: accepted words (project settings). + pub project_words: Vec, + /// Custom dictionaries: plain word lists or Hunspell `.dic` files. + pub custom_dictionaries: Vec, +} + +/// Changes to one inspection; absent fields keep the inspection's defaults. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(default, rename_all = "camelCase")] +pub struct InspectionOverride { + #[specta(optional)] + pub enabled: Option, + #[specta(optional)] + pub severity: Option, +} + +/// IDEA's highlighting levels. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub enum InspectionLevel { + Error, + Warning, + WeakWarning, + ServerProblem, + Typo, + /// Reported (Problems, Inspect Code) but not highlighted in the editor. + None, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn overrides_parse_partially() { + let s: InspectionSettings = serde_json::from_str( + r#"{"default":{"lsp:ruff":{"enabled":false},"lsp:ruff:F401":{"severity":"weakWarning"}}}"#, + ) + .unwrap(); + assert_eq!(s.profile, "default"); + assert_eq!(s.default["lsp:ruff"].enabled, Some(false)); + assert_eq!(s.default["lsp:ruff:F401"].severity, Some(InspectionLevel::WeakWarning)); + assert!(s.project.is_empty()); + assert!(s.ide_profiles.is_empty()); + let s: InspectionSettings = serde_json::from_str( + r#"{"profile":"ide:strict","ideProfiles":{"strict":{"name":"Strict","overrides":{"lsp:ruff":{"severity":"error"}}}}}"#, + ) + .unwrap(); + assert_eq!(s.ide_profiles["strict"].name, "Strict"); + assert_eq!(s.ide_profiles["strict"].overrides["lsp:ruff"].severity, Some(InspectionLevel::Error)); + assert!(serde_json::from_str::(r#"{"default":{"x":{"severity":"loud"}}}"#).is_err()); + } +} diff --git a/crates/ide-core/src/lib.rs b/crates/ide-core/src/lib.rs index 574f2f89..ea2e3721 100644 --- a/crates/ide-core/src/lib.rs +++ b/crates/ide-core/src/lib.rs @@ -9,6 +9,7 @@ pub mod error; pub mod event; pub mod ids; pub mod ignored; +pub mod inspection_settings; pub mod ipc_path; pub mod process; pub mod project_dir; diff --git a/crates/ide-core/src/settings.rs b/crates/ide-core/src/settings.rs index ac68f0b0..24056230 100644 --- a/crates/ide-core/src/settings.rs +++ b/crates/ide-core/src/settings.rs @@ -6,6 +6,7 @@ use specta::Type; use std::collections::BTreeMap; pub use crate::editor_settings::*; +pub use crate::inspection_settings::*; pub use crate::workflow_settings::*; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] @@ -40,6 +41,10 @@ pub struct Settings { /// Language servers / debug adapters at a user-chosen location, by the /// command the language definition launches (`elixir-ls`, `codelldb`). pub tools: BTreeMap, + /// Editor › Inspections profiles. + pub inspections: InspectionSettings, + /// Editor › Natural Languages › Spelling. + pub spelling: SpellingSettings, } #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -77,6 +82,8 @@ impl Default for Settings { actions_on_save: ActionsOnSaveSettings::default(), external_tools: Vec::new(), tools: BTreeMap::new(), + inspections: InspectionSettings::default(), + spelling: SpellingSettings::default(), } } } @@ -109,6 +116,11 @@ pub struct GeneralSettings { pub save_on_idle: bool, pub idle_seconds: u32, pub date_format: DateFormatSettings, + /// File › Power Save Mode: no automatic popups (completion, parameter info, + /// quick documentation) or background highlighting (usages, code vision). + pub power_save_mode: bool, + /// File › Rename Project… (project layer): the name shown for the project; empty = its folder name. + pub project_name: String, } impl Default for GeneralSettings { @@ -121,6 +133,8 @@ impl Default for GeneralSettings { save_on_idle: false, idle_seconds: 15, date_format: DateFormatSettings::default(), + power_save_mode: false, + project_name: String::new(), } } } @@ -210,6 +224,20 @@ pub struct AppearanceSettings { /// Status bar widgets shown (`true`) or hidden (`false`) by the user /// (status bar context menu); widgets not listed use their default. pub status_bar_widgets: BTreeMap, + /// Customize Main Toolbar; `None` = the default toolbar. + pub main_toolbar: Option, +} + +/// Main toolbar entries per side, in order: title widget ids (`project`, +/// `vcs.branch`…) or `action:`. Widgets in neither list nor `hidden` +/// (registered later) keep their default place. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(default, rename_all = "camelCase")] +pub struct MainToolbarSettings { + pub left: Vec, + pub right: Vec, + /// Widgets the user removed. + pub hidden: Vec, } impl Default for AppearanceSettings { @@ -239,6 +267,7 @@ impl Default for AppearanceSettings { show_tool_window_names: true, widescreen_tool_windows: false, status_bar_widgets: BTreeMap::new(), + main_toolbar: None, } } } @@ -273,6 +302,7 @@ pub struct EditorSettings { pub smart_keys: SmartKeysSettings, pub code_editing: CodeEditingSettings, pub inlay: InlayHintsSettings, + pub inline_completion: InlineCompletionSettings, /// Gutter icon groups turned off (`run`, `breakpoints`, `bookmarks`, `vcs`, `blame`). pub gutter_icons_disabled: Vec, pub show_gutter_icons: bool, @@ -307,6 +337,7 @@ impl Default for EditorSettings { smart_keys: SmartKeysSettings::default(), code_editing: CodeEditingSettings::default(), inlay: InlayHintsSettings::default(), + inline_completion: InlineCompletionSettings::default(), gutter_icons_disabled: Vec::new(), show_gutter_icons: true, code_style: CodeStyleSettings::default(), @@ -329,6 +360,22 @@ pub struct FileSettings { pub local_history_days: u32, /// Upper bound for a project's Local History in MB; the oldest revisions go first. pub local_history_max_mb: u32, + /// File Types › Recognized File Types: the user's changes, by language id. + pub file_types: BTreeMap, + /// Override File Type: file (project-relative and `/`-separated, or absolute) → language id. + pub file_type_overrides: BTreeMap, +} + +/// One language's file name and hashbang patterns on top of its definition. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(default, rename_all = "camelCase")] +pub struct FileTypeAssociation { + /// Added file name patterns: `*.foo`, `Jenkinsfile`, `Dockerfile.*` (`*` and `?` wildcards). + pub patterns: Vec, + /// Patterns of the definition (`*.ext`, file names) the user removed. + pub removed_patterns: Vec, + /// Interpreters run by a `#!` first line, e.g. `node` for `#!/usr/bin/env node`. + pub hashbangs: Vec, } impl Default for FileSettings { @@ -341,6 +388,8 @@ impl Default for FileSettings { ensure_final_newline: true, local_history_days: 5, local_history_max_mb: 512, + file_types: BTreeMap::new(), + file_type_overrides: BTreeMap::new(), } } } @@ -471,6 +520,17 @@ pub struct ProjectStructureSettings { pub excluded: Vec, /// Auto-detected roots (from `pom.xml`, `build.gradle`, `Cargo.toml`, …) the user unmarked. pub unmarked: Vec, + /// Content roots besides the project folder (absolute paths), shown and searched with it. + pub content_roots: Vec, + /// "Exclude files": `;`-separated name patterns (`*.log;tmp*`) excluded at any depth. + pub exclude_files: String, +} + +impl ProjectStructureSettings { + /// The "Exclude files" name patterns. + pub fn exclude_file_patterns(&self) -> Vec { + self.exclude_files.split(';').map(str::trim).filter(|p| !p.is_empty()).map(str::to_owned).collect() + } } impl Settings { @@ -486,10 +546,12 @@ impl Settings { out } - /// `files.exclude` plus `files.ignoredNames` (a bare name matches at any depth). + /// `files.exclude`, `files.ignoredNames` and the Project Structure "Exclude files" + /// patterns (a bare name matches at any depth). pub fn file_excludes(&self) -> Vec { let mut out = self.files.exclude.clone(); out.extend(self.files.ignored_names.iter().filter(|n| !n.trim().is_empty()).cloned()); + out.extend(self.project_structure.exclude_file_patterns().into_iter().filter(|p| !p.contains('/'))); out } @@ -524,4 +586,15 @@ mod tests { assert!(k.overrides["file.save"].is_empty()); assert_eq!(serde_json::to_value(Settings::default()).unwrap()["customKeymaps"], serde_json::json!({})); } + + #[test] + fn exclude_files_patterns_join_the_file_excludes() { + let s: Settings = serde_json::from_str( + r#"{"files":{"exclude":[],"ignoredNames":[]},"projectStructure":{"excludeFiles":" *.log ; tmp*;;"}}"#, + ) + .unwrap(); + assert_eq!(s.project_structure.exclude_file_patterns(), vec!["*.log", "tmp*"]); + assert_eq!(s.file_excludes(), vec!["*.log", "tmp*"]); + assert!(Settings::default().files.file_types.is_empty()); + } } diff --git a/crates/ide-core/src/text.rs b/crates/ide-core/src/text.rs index 45b9caa2..9f96ff76 100644 --- a/crates/ide-core/src/text.rs +++ b/crates/ide-core/src/text.rs @@ -129,6 +129,11 @@ pub struct Diagnostic { pub unnecessary: bool, #[serde(default)] pub deprecated: bool, + /// Language server (or tool) that reported it, e.g. `basedpyright`; `source` + /// is what the producer calls itself. Inspection profiles key on this. + #[serde(default)] + #[specta(optional)] + pub server: Option, } /// Symbol kinds, a stable subset of LSP `SymbolKind`. @@ -168,6 +173,17 @@ impl SymbolKind { } } +/// Declared (or conventional) visibility of an outline symbol. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub enum Visibility { + Public, + Protected, + /// Package / module / assembly scope (Java default, `internal`, `pub(crate)`, Go unexported). + Package, + Private, +} + /// A node of a document outline (Structure tool window, breadcrumbs). #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] pub struct OutlineItem { @@ -179,6 +195,10 @@ pub struct OutlineItem { /// The identifier itself; where navigation lands. pub selection_range: Range, pub children: Vec, + /// Unknown (`None`) for language-server symbols and languages without the notion. + #[serde(default)] + #[specta(optional)] + pub visibility: Option, } /// Fuzzy-match highlight: indices (in chars of the displayed string) that matched. diff --git a/crates/ide-coverage/src/import.rs b/crates/ide-coverage/src/import.rs new file mode 100644 index 00000000..669f1b8c --- /dev/null +++ b/crates/ide-coverage/src/import.rs @@ -0,0 +1,173 @@ +//! "Import a report collected in CI from disk": the format is sniffed from the +//! content (lcov, Go cover profile, JaCoCo XML) and paths recorded on another +//! machine are mapped into the project by their longest existing suffix. + +use crate::{Accumulator, CoverageSuite, goprofile, jacoco, lcov}; +use ide_core::{IdeError, IdeResult}; +use std::path::{Component, Path, PathBuf}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum Format { + Lcov, + GoProfile, + Jacoco, +} + +pub(crate) fn detect(text: &str) -> Option { + let head = text.trim_start_matches('\u{feff}').trim_start(); + if head.starts_with('<') { + return head.contains(" IdeResult { + let text = std::fs::read_to_string(path)?; + let mut suite = match detect(&text) { + Some(Format::Lcov) => lcov::parse(&text, root).finish("lcov"), + Some(Format::GoProfile) => { + let (module, module_dir) = go_module_for(root, &text); + goprofile::parse(&text, &module, &module_dir).finish("go test -cover") + } + Some(Format::Jacoco) => { + // A report inside the project (`/target/site/jacoco/…`) knows its module. + let module = jacoco::module_dir(path); + let module = + if path.starts_with(root) && module.starts_with(root) && module != path.parent().unwrap_or(path) { + module + } else { + root.to_path_buf() + }; + let mut acc = Accumulator { has_branches: true, ..Default::default() }; + jacoco::parse_into(&text, &module, &mut acc) + .map_err(|e| IdeError::invalid(format!("{}: {e}", path.display())))?; + acc.finish("JaCoCo") + } + None => { + return Err(IdeError::invalid(format!( + "{} is not an lcov tracefile, Go cover profile or JaCoCo XML report", + path.display() + ))); + } + }; + for f in &mut suite.files { + let p: &Path = f.path.as_ref(); + if !p.exists() + && let Some(found) = relocate(p, root) + { + f.path = found.into(); + } + } + suite.files.sort_by(|a, b| (a.path.as_ref() as &Path).cmp(b.path.as_ref())); + Ok(suite) +} + +/// `/home/runner/work/app/app/src/lib.rs` → `/src/lib.rs`: the longest suffix of +/// `path` that exists under `root`. +pub(crate) fn relocate(path: &Path, root: &Path) -> Option { + let parts: Vec<&std::ffi::OsStr> = path + .components() + .filter_map(|c| match c { + Component::Normal(s) => Some(s), + _ => None, + }) + .collect(); + (0..parts.len()).find_map(|skip| { + let candidate = parts[skip..].iter().fold(root.to_path_buf(), |p, s| p.join(s)); + candidate.is_file().then_some(candidate) + }) +} + +/// The Go module the profile's import paths belong to: the project's `go.mod`, else one +/// a level or two down whose module path prefixes the profile's files. +fn go_module_for(root: &Path, profile: &str) -> (String, PathBuf) { + let first = profile + .lines() + .map(str::trim) + .find(|l| !l.is_empty() && !l.starts_with("mode:")) + .and_then(|l| l.rsplit_once(':').map(|(f, _)| f.to_owned())) + .unwrap_or_default(); + let matches = |module: &str| !module.is_empty() && (first == module || first.starts_with(&format!("{module}/"))); + let read = |dir: &Path| -> Option { + let text = std::fs::read_to_string(dir.join("go.mod")).ok()?; + text.lines().find_map(|l| l.trim().strip_prefix("module")).map(|m| m.trim().trim_matches('"').to_owned()) + }; + let mut candidates = vec![root.to_path_buf()]; + for dir in std::fs::read_dir(root).into_iter().flatten().flatten().map(|e| e.path()).filter(|p| p.is_dir()) { + candidates + .extend(std::fs::read_dir(&dir).into_iter().flatten().flatten().map(|e| e.path()).filter(|p| p.is_dir())); + candidates.push(dir); + } + let found: Vec<(String, PathBuf)> = candidates.into_iter().filter_map(|d| read(&d).map(|m| (m, d))).collect(); + found + .iter() + .find(|(m, _)| matches(m)) + .or_else(|| found.iter().find(|(_, d)| d == root)) + .cloned() + .unwrap_or_else(|| (String::new(), root.to_path_buf())) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn detects_formats() { + assert_eq!(detect("TN:\nSF:a.rs\nDA:1,1\nend_of_record\n"), Some(Format::Lcov)); + assert_eq!(detect("mode: atomic\nm/a.go:1.1,2.2 1 0\n"), Some(Format::GoProfile)); + assert_eq!( + detect(""), + Some(Format::Jacoco) + ); + assert_eq!(detect(""), None); + assert_eq!(detect("hello"), None); + } + + #[test] + fn imports_ci_lcov_with_foreign_paths() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path().join("app"); + std::fs::create_dir_all(root.join("src")).unwrap(); + std::fs::write(root.join("src/lib.rs"), "").unwrap(); + let report = tmp.path().join("lcov.info"); + std::fs::write(&report, "SF:/home/runner/work/app/app/src/lib.rs\nDA:1,1\nDA:2,0\nend_of_record\n").unwrap(); + let suite = import(&report, &root).unwrap(); + assert_eq!(suite.runner, "lcov"); + assert_eq!(suite.files[0].path.as_ref() as &Path, root.join("src/lib.rs")); + assert_eq!(suite.files[0].lines_covered, 1); + assert!(import(&tmp.path().join("missing"), &root).is_err()); + std::fs::write(&report, "nothing").unwrap(); + assert!(import(&report, &root).is_err()); + } + + #[test] + fn imports_go_profiles_of_nested_modules() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + std::fs::create_dir_all(root.join("svc/calc")).unwrap(); + std::fs::write(root.join("svc/go.mod"), "module example.com/svc\n").unwrap(); + let report = root.join("cover.out"); + std::fs::write(&report, "mode: set\nexample.com/svc/calc/calc.go:3.1,4.2 1 1\n").unwrap(); + let suite = import(&report, root).unwrap(); + assert_eq!(suite.files[0].path.as_ref() as &Path, root.join("svc/calc/calc.go")); + } + + #[test] + fn imports_jacoco_against_the_project() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path().join("p"); + std::fs::create_dir_all(root.join("src/main/java/com/x")).unwrap(); + std::fs::write(root.join("src/main/java/com/x/A.java"), "").unwrap(); + let report = tmp.path().join("jacoco.xml"); + let xml = r#""#; + std::fs::write(&report, xml).unwrap(); + let suite = import(&report, &root).unwrap(); + assert_eq!(suite.files[0].path.as_ref() as &Path, root.join("src/main/java/com/x/A.java")); + assert!(suite.has_branches); + } +} diff --git a/crates/ide-coverage/src/lib.rs b/crates/ide-coverage/src/lib.rs index ccb1c308..0df488bf 100644 --- a/crates/ide-coverage/src/lib.rs +++ b/crates/ide-coverage/src/lib.rs @@ -16,10 +16,12 @@ //! Lines in the model are 0-based, like every IPC coordinate. mod goprofile; +mod import; mod jacoco; mod lcov; mod plan; +pub use import::import; pub use plan::prepare; use ide_core::{IdeError, IdeResult, IpcPath}; diff --git a/crates/ide-coverage/src/plan.rs b/crates/ide-coverage/src/plan.rs index 4d3ebcc6..2368aaaa 100644 --- a/crates/ide-coverage/src/plan.rs +++ b/crates/ide-coverage/src/plan.rs @@ -288,6 +288,7 @@ mod tests { is_test: true, debug_adapter: None, allow_parallel: false, + extras: Default::default(), } } diff --git a/crates/ide-db/src/conn.rs b/crates/ide-db/src/conn.rs index 8e7d6c75..95b3ff60 100644 --- a/crates/ide-db/src/conn.rs +++ b/crates/ide-db/src/conn.rs @@ -15,7 +15,8 @@ use sqlx::{ postgres::{PgConnectOptions, PgPool, PgPoolOptions, PgSslMode}, sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}, }; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::sync::Arc; use std::time::{Duration, Instant}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -184,6 +185,90 @@ fn looks_like_query(sql: &str) -> bool { ) } +fn file_setting(path: &str) -> Option { + let path = path.trim(); + (!path.is_empty()).then(|| crate::tunnel::expand(path)) +} + +fn is_identifier(name: &str) -> bool { + let mut chars = name.chars(); + chars.next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_') + && chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.') +} + +/// A MySQL `SET` value: numbers and keywords as they are, anything else quoted. +fn mysql_value(v: &str) -> String { + let keyword = ["ON", "OFF", "TRUE", "FALSE", "DEFAULT"].iter().any(|k| k.eq_ignore_ascii_case(v)); + if keyword || v.parse::().is_ok() { + v.to_owned() + } else { + format!("'{}'", v.replace('\\', "\\\\").replace('\'', "''")) + } +} + +/// How the Options and Advanced tabs reach a connection. +#[derive(Debug, Default, PartialEq, Eq)] +pub(crate) struct Setup { + /// PostgreSQL run-time parameters (startup `options`), SQLite pragmas. + pub params: Vec<(String, String)>, + /// Run on every new connection: MySQL session settings, then the startup script. + pub statements: Vec, + pub application_name: Option, + pub charset: Option, + pub collation: Option, + pub statement_cache: Option, + pub connect_timeout: Option, +} + +impl Setup { + pub fn of(source: &DataSource) -> IdeResult { + let mut s = Setup::default(); + let o = &source.options; + match source.driver { + DbDriver::Postgres => { + if source.read_only { + s.params.push(("default_transaction_read_only".into(), "on".into())); + } + if !o.default_schema.trim().is_empty() { + s.params.push(("search_path".into(), o.default_schema.trim().into())); + } + if !o.time_zone.trim().is_empty() { + s.params.push(("TimeZone".into(), o.time_zone.trim().into())); + } + } + DbDriver::Mysql if source.read_only => s.statements.push("SET SESSION TRANSACTION READ ONLY".into()), + _ => {} + } + for p in &source.properties { + let value = p.value.trim(); + let name = p.name.trim().replace('-', "_"); + if name.is_empty() { + continue; + } + if !is_identifier(&name) { + return Err(IdeError::invalid(format!("invalid driver property name: {}", p.name.trim()))); + } + let number = |what: &str| { + value.parse::().map_err(|_| IdeError::invalid(format!("{what} must be a whole number: {value}"))) + }; + match (source.driver, name.to_ascii_lowercase().as_str()) { + (_, "statement_cache_capacity") => s.statement_cache = Some(number(&name)? as usize), + (_, "connect_timeout") => s.connect_timeout = Some(Duration::from_secs(number(&name)?)), + (DbDriver::Postgres, "application_name") => s.application_name = Some(value.to_owned()), + (DbDriver::Mysql, "charset") => s.charset = Some(value.to_owned()), + (DbDriver::Mysql, "collation") => s.collation = Some(value.to_owned()), + (DbDriver::Mysql, _) => s.statements.push(format!("SET SESSION {name} = {}", mysql_value(value))), + _ => s.params.push((name, value.to_owned())), + } + } + let script = o.startup_script.trim(); + if !script.is_empty() { + s.statements.extend(crate::split::split(script, source.driver).into_iter().map(|st| st.text)); + } + Ok(s) + } +} + impl Pool { pub fn driver(&self) -> DbDriver { match self { @@ -196,7 +281,24 @@ impl Pool { /// Opens a pool and one connection (so bad settings fail here). pub async fn connect(source: &DataSource, password: Option<&str>, root: Option<&Path>) -> IdeResult { const MAX: u32 = 4; - let timeout = Duration::from_secs(30); + let setup = Setup::of(source)?; + let timeout = setup.connect_timeout.unwrap_or(Duration::from_secs(30)); + let init = Arc::new(setup.statements); + // Runs the session statements (startup script…) on every new connection. + macro_rules! after_connect { + () => {{ + let init = init.clone(); + move |conn, _| { + let init = init.clone(); + Box::pin(async move { + for sql in init.iter() { + sqlx::raw_sql(sqlx::AssertSqlSafe(sql.clone())).execute(&mut *conn).await?; + } + Ok(()) + }) + } + }}; + } match source.driver { DbDriver::Sqlite => { let file = source.file.as_deref().ok_or_else(|| IdeError::invalid("no database file"))?; @@ -204,11 +306,18 @@ impl Pool { Some(r) if file.is_relative() => r.join(file), _ => file.to_path_buf(), }; - let opts = + let mut opts = SqliteConnectOptions::new().filename(&file).create_if_missing(false).read_only(source.read_only); + for (k, v) in setup.params { + opts = opts.pragma(k, v); + } + if let Some(n) = setup.statement_cache { + opts = opts.statement_cache_capacity(n); + } let pool = SqlitePoolOptions::new() .max_connections(MAX) .acquire_timeout(timeout) + .after_connect(after_connect!()) .connect_with(opts) .await .map_err(db_err)?; @@ -219,24 +328,39 @@ impl Pool { .host(&source.host) .port(source.port.unwrap_or(5432)) .username(&source.user) - .application_name("OpenIDEA") + .application_name(setup.application_name.as_deref().unwrap_or("OpenIDEA")) .ssl_mode(match source.ssl { DbSslMode::Disable => PgSslMode::Disable, DbSslMode::Prefer => PgSslMode::Prefer, DbSslMode::Require => PgSslMode::Require, + DbSslMode::VerifyCa => PgSslMode::VerifyCa, + DbSslMode::VerifyFull => PgSslMode::VerifyFull, }); + if let Some(f) = file_setting(&source.ssl_ca_file) { + opts = opts.ssl_root_cert(f); + } + if let Some(f) = file_setting(&source.ssl_cert_file) { + opts = opts.ssl_client_cert(f); + } + if let Some(f) = file_setting(&source.ssl_key_file) { + opts = opts.ssl_client_key(f); + } if !source.database.is_empty() { opts = opts.database(&source.database); } if let Some(p) = password { opts = opts.password(p); } - if source.read_only { - opts = opts.options([("default_transaction_read_only", "on")]); + if !setup.params.is_empty() { + opts = opts.options(setup.params); + } + if let Some(n) = setup.statement_cache { + opts = opts.statement_cache_capacity(n); } let pool = PgPoolOptions::new() .max_connections(MAX) .acquire_timeout(timeout) + .after_connect(after_connect!()) .connect_with(opts) .await .map_err(db_err)?; @@ -251,25 +375,41 @@ impl Pool { DbSslMode::Disable => MySqlSslMode::Disabled, DbSslMode::Prefer => MySqlSslMode::Preferred, DbSslMode::Require => MySqlSslMode::Required, + DbSslMode::VerifyCa => MySqlSslMode::VerifyCa, + DbSslMode::VerifyFull => MySqlSslMode::VerifyIdentity, }); + if let Some(f) = file_setting(&source.ssl_ca_file) { + opts = opts.ssl_ca(f); + } + if let Some(f) = file_setting(&source.ssl_cert_file) { + opts = opts.ssl_client_cert(f); + } + if let Some(f) = file_setting(&source.ssl_key_file) { + opts = opts.ssl_client_key(f); + } if !source.database.is_empty() { opts = opts.database(&source.database); } if let Some(p) = password { opts = opts.password(p); } - let read_only = source.read_only; + if let Some(c) = &setup.charset { + opts = opts.charset(c); + } + if let Some(c) = &setup.collation { + opts = opts.collation(c); + } + if let Some(n) = setup.statement_cache { + opts = opts.statement_cache_capacity(n); + } + let tz = source.options.time_zone.trim(); + if !tz.is_empty() { + opts = opts.timezone(Some(tz.to_owned())); + } let pool = MySqlPoolOptions::new() .max_connections(MAX) .acquire_timeout(timeout) - .after_connect(move |conn, _| { - Box::pin(async move { - if read_only { - conn.execute("SET SESSION TRANSACTION READ ONLY").await?; - } - Ok(()) - }) - }) + .after_connect(after_connect!()) .connect_with(opts) .await .map_err(db_err)?; @@ -282,6 +422,10 @@ impl Pool { with_pool!(self, p => p.close().await) } + pub fn is_closed(&self) -> bool { + with_pool!(self, p => p.is_closed()) + } + /// Runs `sql` (no row limit beyond `max`), for introspection. pub async fn rows(&self, sql: &str, max: usize) -> IdeResult>>> { let fetched: IdeResult = with_pool!(self, p => fetch!(p, sql, max)); @@ -434,3 +578,68 @@ fn statement_columns(s: &S) -> Vec { .map(|c| ResultColumn { name: c.name().to_owned(), type_name: c.type_info().name().to_owned() }) .collect() } + +#[cfg(test)] +mod tests { + use super::*; + use crate::source::{DbOptions, DbProperty}; + + fn prop(name: &str, value: &str) -> DbProperty { + DbProperty { name: name.into(), value: value.into() } + } + + #[test] + fn postgres_options_become_runtime_parameters() { + let s = DataSource { + driver: DbDriver::Postgres, + read_only: true, + options: DbOptions { + default_schema: "app, public".into(), + time_zone: "Europe/Paris".into(), + startup_script: "SET work_mem = '64MB'; select 1;".into(), + ..Default::default() + }, + properties: vec![prop("statement_timeout", "5s"), prop("application_name", "me"), prop("", "x")], + ..Default::default() + }; + let setup = Setup::of(&s).unwrap(); + let names: Vec<_> = setup.params.iter().map(|(k, _)| k.as_str()).collect(); + assert_eq!(names, ["default_transaction_read_only", "search_path", "TimeZone", "statement_timeout"]); + assert_eq!(setup.application_name.as_deref(), Some("me")); + assert_eq!(setup.statements, ["SET work_mem = '64MB'", "select 1"]); + } + + #[test] + fn mysql_properties_become_session_variables() { + let s = DataSource { + driver: DbDriver::Mysql, + read_only: true, + properties: vec![ + prop("sql_mode", "ANSI_QUOTES"), + prop("max_execution_time", "1000"), + prop("charset", "utf8mb4"), + prop("connect-timeout", "5"), + ], + ..Default::default() + }; + let setup = Setup::of(&s).unwrap(); + assert_eq!( + setup.statements, + [ + "SET SESSION TRANSACTION READ ONLY", + "SET SESSION sql_mode = 'ANSI_QUOTES'", + "SET SESSION max_execution_time = 1000" + ] + ); + assert_eq!(setup.charset.as_deref(), Some("utf8mb4")); + assert_eq!(setup.connect_timeout, Some(Duration::from_secs(5))); + assert!(Setup::of(&DataSource { properties: vec![prop("a; drop", "1")], ..s.clone() }).is_err()); + assert!(Setup::of(&DataSource { properties: vec![prop("connect_timeout", "soon")], ..s }).is_err()); + } + + #[test] + fn sqlite_properties_become_pragmas() { + let s = DataSource { properties: vec![prop("cache_size", "-2000")], ..Default::default() }; + assert_eq!(Setup::of(&s).unwrap().params, [("cache_size".to_owned(), "-2000".to_owned())]); + } +} diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 1b3e05b7..b115e629 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -10,7 +10,9 @@ pub mod sql; pub mod tunnel; pub use conn::{Held, Pool, QueryResult, ResultColumn}; -pub use source::{DataSource, DbDriver, DbSsh, DbSshAuth, DbSslMode}; +pub use source::{ + DataSource, DbColor, DbDriver, DbIntrospection, DbOptions, DbProperty, DbSsh, DbSshAuth, DbSslMode, DbTxMode, +}; pub use sql::{CellValue, DbRowEdit, SortKey}; use ide_core::{CancellationToken, IdeError, IdeResult, error::ErrorKind}; @@ -21,6 +23,7 @@ use specta::Type; use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; +use std::time::Duration; use tunnel::{OpenError, Tunnel, TunnelSpec, UnknownHostKey}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -219,6 +222,24 @@ fn through_tunnel(e: IdeError, source: &DataSource, tunneled: bool) -> IdeError ) } +/// Options › Run keep-alive query: pings the pool until it is closed or dropped +/// (a dropped SSH tunnel or an idle-killing proxy stays alive meanwhile). +fn keep_alive(pool: &Arc, every: Duration) { + let pool = Arc::downgrade(pool); + tokio::spawn(async move { + let mut tick = tokio::time::interval(every); + tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); + tick.tick().await; + loop { + tick.tick().await; + let Some(p) = pool.upgrade().filter(|p| !p.is_closed()) else { break }; + if let Err(e) = p.rows("SELECT 1", 1).await { + tracing::debug!("keep-alive: {}", e.message); + } + } + }); +} + /// Rolls back a session's transaction and releases its connection. async fn end_session(session: &Session) { let held = session.lock().await.take(); @@ -261,14 +282,16 @@ impl DbManager { source::load(&self.root) } - /// The data source of connection target `target`, with its database for `:`. + /// The connection settings of target `target` ([`DataSource::resolved`]), with + /// its database for `:`. pub fn source(&self, target: &str) -> IdeResult { let (id, database) = split_target(target); let mut source = self .sources()? .into_iter() .find(|s| s.id == id) - .ok_or_else(|| IdeError::not_found(format!("no data source {id}")))?; + .ok_or_else(|| IdeError::not_found(format!("no data source {id}")))? + .resolved()?; if let Some(db) = database { source.database = db.to_owned(); } @@ -341,7 +364,10 @@ impl DbManager { } async fn password(&self, source: &DataSource) -> Option { - self.secret(source.id.clone(), source.driver != DbDriver::Sqlite && source.save_password).await + match self.secret(source.id.clone(), source.driver != DbDriver::Sqlite && source.save_password).await { + Some(p) => Some(p), + None => source.url_password(), + } } /// Trusts the host key that failed the last connection of `id` (adds it to `known_hosts`). @@ -411,6 +437,7 @@ impl DbManager { password: Option, ssh_secret: Option, ) -> IdeResult { + let source = &source.resolved()?; let password = match password { Some(p) => Some(p), None => self.password(source).await, @@ -458,9 +485,14 @@ impl DbManager { return Err(through_tunnel(e, &source, tunnel.is_some())); } }; - let mut pools = self.pools.lock(); // Another caller may have connected meanwhile; keep theirs. - Ok(pools.entry(id.to_owned()).or_insert(pool).clone()) + let kept = self.pools.lock().entry(id.to_owned()).or_insert_with(|| pool.clone()).clone(); + if Arc::ptr_eq(&kept, &pool) + && let Some(secs) = source.options.keep_alive_secs.filter(|s| *s > 0) + { + keep_alive(&pool, Duration::from_secs(secs.into())); + } + Ok(kept) } /// Closes the tunnel of `source_id` once none of its targets is connected. @@ -496,24 +528,29 @@ impl DbManager { /// The databases of the server of data source `id` (PostgreSQL), the connected one first. pub async fn databases(&self, id: &str) -> IdeResult { - let pool = self.pool(split_target(id).0).await?; + let id = split_target(id).0; + let system = self.source(id)?.introspection.show_system; + let pool = self.pool(id).await?; if pool.driver() != DbDriver::Postgres { return Err(IdeError::invalid("only PostgreSQL data sources list databases")); } let first = |rows: Vec>>| rows.into_iter().filter_map(|r| r.into_iter().next().flatten()); let current = first(pool.rows("SELECT current_database()", 1).await?).next().unwrap_or_default(); - let rows = pool.rows(sql::DATABASES, MAX_META_ROWS).await?; + let rows = pool.rows(sql::databases(system), MAX_META_ROWS).await?; let mut names: Vec = - first(rows).filter(|n| *n == current || !sql::INTERNAL_DATABASES.contains(&n.as_str())).collect(); + first(rows).filter(|n| system || *n == current || !sql::INTERNAL_DATABASES.contains(&n.as_str())).collect(); if !names.contains(¤t) { names.insert(0, current.clone()); } Ok(Databases { current, names }) } + /// Every schema (the Schemas tab picks the ones the tree shows); system schemas + /// only with [`DbIntrospection::show_system`]. pub async fn schemas(&self, id: &str) -> IdeResult> { + let system = self.source(id)?.introspection.show_system; let pool = self.pool(id).await?; - let rows = pool.rows(sql::schemas(pool.driver()), MAX_META_ROWS).await?; + let rows = pool.rows(sql::schemas(pool.driver(), system), MAX_META_ROWS).await?; Ok(rows.into_iter().filter_map(|r| r.into_iter().next().flatten()).collect()) } diff --git a/crates/ide-db/src/source.rs b/crates/ide-db/src/source.rs index c691ad34..1036995b 100644 --- a/crates/ide-db/src/source.rs +++ b/crates/ide-db/src/source.rs @@ -35,6 +35,76 @@ pub enum DbSslMode { #[default] Prefer, Require, + /// Require, and check the server certificate against the CA file. + VerifyCa, + /// [`DbSslMode::VerifyCa`], and check that the certificate names the host. + VerifyFull, +} + +/// IDEA's data source colours (tree node and console tab tint), e.g. red for production. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub enum DbColor { + Red, + Orange, + Yellow, + Green, + Blue, + Violet, +} + +/// Transaction control of the data source's consoles. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub enum DbTxMode { + #[default] + Auto, + Manual, +} + +/// Options tab: session settings applied to every connection. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase", default)] +pub struct DbOptions { + pub transaction: DbTxMode, + /// PostgreSQL `search_path` (unqualified names resolve there). + pub default_schema: String, + /// Session time zone (`TimeZone` / `time_zone`); empty keeps the default. + pub time_zone: String, + /// Statements run on every new connection. + pub startup_script: String, + /// Run `SELECT 1` this often while connected. + pub keep_alive_secs: Option, + /// Disconnect after this long without statements (enforced by the UI, which sees the activity). + pub auto_disconnect_secs: Option, +} + +/// Schemas tab: the databases / schemas the tree shows. Names may use `*` and `?`. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase", default)] +pub struct DbIntrospection { + pub all_databases: bool, + pub databases: Vec, + pub all_schemas: bool, + pub schemas: Vec, + /// `pg_catalog`, `information_schema`, `mysql`…, and PostgreSQL template databases. + pub show_system: bool, +} + +impl Default for DbIntrospection { + fn default() -> Self { + Self { all_databases: true, databases: Vec::new(), all_schemas: true, schemas: Vec::new(), show_system: false } + } +} + +/// Advanced tab: a driver property. PostgreSQL: run-time parameters (and +/// `application_name`); MySQL: session variables (and `charset`, `collation`); +/// SQLite: pragmas. See [`crate::conn`]. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase", default)] +pub struct DbProperty { + pub name: String, + pub value: String, } /// How the SSH tunnel authenticates (IDEA's SSH configuration "Authentication type"). @@ -84,6 +154,20 @@ pub struct DataSource { pub read_only: bool, /// SSH tunnel settings (`None`: never configured; kept but unused when not `enabled`). pub ssh: Option, + /// Wire-compatible product preset (`cockroachdb`, `tidb`…; empty: the driver itself). + pub vendor: String, + pub color: Option, + pub comment: String, + /// Connect with [`DataSource::url`] instead of the host / port / database fields. + pub url_only: bool, + pub url: String, + /// PEM files; `~/` is the home directory. + pub ssl_ca_file: String, + pub ssl_cert_file: String, + pub ssl_key_file: String, + pub options: DbOptions, + pub introspection: DbIntrospection, + pub properties: Vec, } impl Default for DataSource { @@ -101,10 +185,188 @@ impl Default for DataSource { save_password: false, read_only: false, ssh: None, + vendor: String::new(), + color: None, + comment: String::new(), + url_only: false, + url: String::new(), + ssl_ca_file: String::new(), + ssl_cert_file: String::new(), + ssl_key_file: String::new(), + options: DbOptions::default(), + introspection: DbIntrospection::default(), + properties: Vec::new(), } } } +/// The connection settings of a URL (`jdbc:` prefix allowed). +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ParsedUrl { + pub driver: Option, + pub host: String, + pub port: Option, + pub database: String, + pub user: String, + pub password: Option, + /// Query parameters, in order. + pub params: Vec<(String, String)>, + /// SQLite file. + pub file: Option, +} + +fn percent_decode(s: &str) -> String { + let b = s.as_bytes(); + let mut out = Vec::with_capacity(b.len()); + let mut i = 0; + let hex = |i: usize| b.get(i).and_then(|&c| (c as char).to_digit(16)); + while i < b.len() { + if b[i] == b'%' + && let (Some(h), Some(l)) = (hex(i + 1), hex(i + 2)) + { + out.push((h * 16 + l) as u8); + i += 3; + continue; + } + out.push(b[i]); + i += 1; + } + String::from_utf8_lossy(&out).into_owned() +} + +/// The driver speaking the protocol of URL scheme `scheme` (wire-compatible products included). +pub fn scheme_driver(scheme: &str) -> Option { + match scheme.to_ascii_lowercase().as_str() { + "postgres" | "postgresql" | "pgsql" | "redshift" | "cockroachdb" | "cockroach" | "yugabytedb" + | "timescaledb" | "greenplum" => Some(DbDriver::Postgres), + "mysql" | "mariadb" | "tidb" | "singlestore" | "memsql" | "oceanbase" => Some(DbDriver::Mysql), + "sqlite" | "sqlite3" => Some(DbDriver::Sqlite), + _ => None, + } +} + +/// Parses `scheme://[user[:password]@]host[:port][/database][?k=v&…]`, `jdbc:` forms +/// included, and `sqlite:path`. +pub fn parse_url(raw: &str) -> IdeResult { + let bad = || IdeError::invalid(format!("not a database URL: {raw}")); + let url = raw.trim(); + let url = url.strip_prefix("jdbc:").or_else(|| url.strip_prefix("JDBC:")).unwrap_or(url); + let (scheme, rest) = url.split_once(':').ok_or_else(bad)?; + let driver = scheme_driver(scheme).ok_or_else(bad)?; + if driver == DbDriver::Sqlite { + let file = rest.strip_prefix("//").unwrap_or(rest); + let file = file.split('?').next().unwrap_or_default(); + if file.is_empty() { + return Err(bad()); + } + return Ok(ParsedUrl { driver: Some(driver), file: Some(percent_decode(file)), ..Default::default() }); + } + let rest = rest.strip_prefix("//").ok_or_else(bad)?; + let (main, query) = rest.split_once('?').unwrap_or((rest, "")); + let (authority, database) = main.split_once('/').unwrap_or((main, "")); + let (userinfo, hostport) = match authority.rsplit_once('@') { + Some((u, h)) => (Some(u), h), + None => (None, authority), + }; + // Several hosts (failover lists): the first. + let hostport = hostport.split(',').next().unwrap_or_default(); + let (host, port) = if let Some(v6) = hostport.strip_prefix('[') { + let (h, after) = v6.split_once(']').ok_or_else(bad)?; + (h.to_owned(), after.strip_prefix(':')) + } else { + match hostport.rsplit_once(':') { + Some((h, p)) => (h.to_owned(), Some(p)), + None => (hostport.to_owned(), None), + } + }; + let port = match port.filter(|p| !p.is_empty()) { + Some(p) => Some(p.parse::().map_err(|_| bad())?), + None => None, + }; + let mut parsed = ParsedUrl { + driver: Some(driver), + host, + port, + database: percent_decode(database.trim_end_matches('/')), + ..Default::default() + }; + if let Some(u) = userinfo { + let (user, password) = match u.split_once(':') { + Some((u, p)) => (u, Some(percent_decode(p))), + None => (u, None), + }; + parsed.user = percent_decode(user); + parsed.password = password; + } + for pair in query.split('&').filter(|p| !p.is_empty()) { + let (k, v) = pair.split_once('=').unwrap_or((pair, "")); + let (k, v) = (percent_decode(k), percent_decode(v)); + match k.as_str() { + "user" if parsed.user.is_empty() => parsed.user = v, + "password" if parsed.password.is_none() => parsed.password = Some(v), + _ => parsed.params.push((k, v)), + } + } + Ok(parsed) +} + +impl DataSource { + /// The settings to connect with: with [`DataSource::url_only`], host, port, + /// database, user, SSL and driver properties come from the URL (properties + /// of the Advanced tab still apply after the URL's). + pub fn resolved(&self) -> IdeResult { + if !self.url_only { + return Ok(self.clone()); + } + let u = parse_url(&self.url)?; + let mut s = self.clone(); + if let Some(d) = u.driver + && d != s.driver + { + return Err(IdeError::invalid(format!("the URL is not for the {:?} driver", s.driver))); + } + if s.driver == DbDriver::Sqlite { + s.file = u.file.map(IpcPath::from); + return Ok(s); + } + s.host = u.host; + s.port = u.port; + s.database = u.database; + if !u.user.is_empty() { + s.user = u.user; + } + let mut properties = Vec::new(); + for (k, v) in u.params { + match k.to_ascii_lowercase().as_str() { + "sslmode" | "ssl-mode" => { + s.ssl = match v.to_ascii_lowercase().replace('-', "_").as_str() { + "disable" | "disabled" => DbSslMode::Disable, + "require" | "required" => DbSslMode::Require, + "verify_ca" => DbSslMode::VerifyCa, + "verify_full" | "verify_identity" => DbSslMode::VerifyFull, + _ => DbSslMode::Prefer, + } + } + "sslrootcert" | "ssl-ca" | "sslca" => s.ssl_ca_file = v, + "sslcert" | "ssl-cert" => s.ssl_cert_file = v, + "sslkey" | "ssl-key" => s.ssl_key_file = v, + _ => properties.push(DbProperty { name: k, value: v }), + } + } + properties.append(&mut s.properties); + s.properties = properties; + Ok(s) + } + + /// The password written in the URL of a [`DataSource::url_only`] data source. + pub fn url_password(&self) -> Option { + if !self.url_only { + return None; + } + parse_url(&self.url).ok()?.password + } +} + #[derive(Debug, Default, Serialize, Deserialize)] struct SourcesFile { #[serde(default, rename = "data-source")] @@ -164,4 +426,77 @@ mod tests { assert_eq!(load(tmp.path()).unwrap(), vec![s]); assert!(save(tmp.path(), &[DataSource::default()]).is_err()); } + + #[test] + fn reads_configs_written_before_the_new_settings() { + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path().join(".openidea"); + std::fs::create_dir_all(&dir).unwrap(); + std::fs::write( + dir.join(FILE), + "[[data-source]]\nid = \"a\"\nname = \"old\"\ndriver = \"postgres\"\nhost = \"db\"\nssl = \"require\"\n", + ) + .unwrap(); + let s = load(tmp.path()).unwrap().remove(0); + assert_eq!(s.ssl, DbSslMode::Require); + assert!(s.introspection.all_databases && s.introspection.all_schemas); + assert_eq!((s.color, s.url_only, s.options.transaction), (None, false, DbTxMode::Auto)); + } + + #[test] + fn round_trips_the_new_settings() { + let tmp = tempfile::tempdir().unwrap(); + let s = DataSource { + id: "b".into(), + name: "prod".into(), + driver: DbDriver::Mysql, + vendor: "tidb".into(), + color: Some(DbColor::Red), + comment: "careful".into(), + ssl: DbSslMode::VerifyFull, + ssl_ca_file: "~/ca.pem".into(), + options: DbOptions { time_zone: "UTC".into(), keep_alive_secs: Some(60), ..Default::default() }, + introspection: DbIntrospection { all_schemas: false, schemas: vec!["app*".into()], ..Default::default() }, + properties: vec![DbProperty { name: "sql_mode".into(), value: "ANSI".into() }], + ..Default::default() + }; + save(tmp.path(), std::slice::from_ref(&s)).unwrap(); + assert_eq!(load(tmp.path()).unwrap(), vec![s]); + } + + #[test] + fn parses_urls() { + let u = parse_url("jdbc:postgresql://ann:s%40cret@db.local:5433/shop?sslmode=require&search_path=app").unwrap(); + assert_eq!(u.driver, Some(DbDriver::Postgres)); + assert_eq!((u.host.as_str(), u.port, u.database.as_str()), ("db.local", Some(5433), "shop")); + assert_eq!((u.user.as_str(), u.password.as_deref()), ("ann", Some("s@cret"))); + assert_eq!(u.params, vec![("sslmode".into(), "require".into()), ("search_path".into(), "app".into())]); + let u = parse_url("mysql://[::1]/app?user=root").unwrap(); + assert_eq!((u.host.as_str(), u.port, u.user.as_str()), ("::1", None, "root")); + assert_eq!(parse_url("jdbc:redshift://r:5439/dev").unwrap().driver, Some(DbDriver::Postgres)); + assert_eq!(parse_url("tidb://h:4000").unwrap().driver, Some(DbDriver::Mysql)); + assert_eq!(parse_url("sqlite:/tmp/a.db").unwrap().file.as_deref(), Some("/tmp/a.db")); + assert!(parse_url("oracle://x").is_err()); + assert!(parse_url("postgres://h:99999/db").is_err()); + } + + #[test] + fn url_only_sources_connect_with_the_url() { + let s = DataSource { + driver: DbDriver::Postgres, + user: "fallback".into(), + url_only: true, + url: "postgres://ann:pw@h:6543/app?sslmode=verify-full&sslrootcert=/ca.pem&statement_timeout=5000".into(), + properties: vec![DbProperty { name: "work_mem".into(), value: "64MB".into() }], + ..Default::default() + }; + let r = s.resolved().unwrap(); + assert_eq!((r.host.as_str(), r.port, r.database.as_str(), r.user.as_str()), ("h", Some(6543), "app", "ann")); + assert_eq!((r.ssl, r.ssl_ca_file.as_str()), (DbSslMode::VerifyFull, "/ca.pem")); + let names: Vec<_> = r.properties.iter().map(|p| p.name.as_str()).collect(); + assert_eq!(names, ["statement_timeout", "work_mem"]); + assert_eq!(s.url_password().as_deref(), Some("pw")); + assert!(DataSource { url: "mysql://h/db".into(), ..s.clone() }.resolved().is_err()); + assert_eq!(DataSource { url_only: false, ..s }.resolved().unwrap().host, "localhost"); + } } diff --git a/crates/ide-db/src/sql.rs b/crates/ide-db/src/sql.rs index f76dfb0a..17373158 100644 --- a/crates/ide-db/src/sql.rs +++ b/crates/ide-db/src/sql.rs @@ -33,14 +33,26 @@ pub fn qualified(driver: DbDriver, schema: &str, table: &str) -> String { } /// Databases one can connect to (PostgreSQL). -pub const DATABASES: &str = "SELECT datname FROM pg_database WHERE NOT datistemplate AND datallowconn ORDER BY datname"; +/// PostgreSQL databases one can connect to; templates only with `system`. +pub fn databases(system: bool) -> &'static str { + if system { + "SELECT datname FROM pg_database WHERE datallowconn ORDER BY datname" + } else { + "SELECT datname FROM pg_database WHERE NOT datistemplate AND datallowconn ORDER BY datname" + } +} /// Databases of managed services that their users cannot use (hidden unless connected to). pub const INTERNAL_DATABASES: &[&str] = &["rdsadmin", "azure_maintenance", "azure_sys", "cloudsqladmin"]; -pub fn schemas(driver: DbDriver) -> &'static str { +pub fn schemas(driver: DbDriver, system: bool) -> &'static str { match driver { DbDriver::Sqlite => "SELECT name FROM pragma_database_list WHERE name <> 'temp' ORDER BY seq", + DbDriver::Postgres if system => { + "SELECT schema_name FROM information_schema.schemata \ + WHERE schema_name NOT LIKE 'pg\\_toast%' AND schema_name NOT LIKE 'pg\\_temp%' ORDER BY 1" + } + DbDriver::Mysql if system => "SELECT schema_name FROM information_schema.schemata ORDER BY 1", DbDriver::Postgres => { "SELECT schema_name FROM information_schema.schemata \ WHERE schema_name NOT IN ('pg_catalog', 'information_schema') \ diff --git a/crates/ide-db/src/tunnel.rs b/crates/ide-db/src/tunnel.rs index 84539a5e..8a6f60a6 100644 --- a/crates/ide-db/src/tunnel.rs +++ b/crates/ide-db/src/tunnel.rs @@ -200,7 +200,7 @@ fn home() -> Option { } /// `~/x` → `$HOME/x`. -fn expand(path: &str) -> PathBuf { +pub(crate) fn expand(path: &str) -> PathBuf { match (path.strip_prefix("~/"), home()) { (Some(rest), Some(h)) => h.join(rest), _ => PathBuf::from(path), diff --git a/crates/ide-db/tests/sqlite.rs b/crates/ide-db/tests/sqlite.rs index 3dcc60b9..03d12dac 100644 --- a/crates/ide-db/tests/sqlite.rs +++ b/crates/ide-db/tests/sqlite.rs @@ -1,7 +1,7 @@ //! Introspection, queries and table edits against a real SQLite file. use ide_core::{CancellationToken, IdeResult}; -use ide_db::{CellValue, DataSource, DbDriver, DbManager, DbRowEdit, DbTableKind, SortKey}; +use ide_db::{CellValue, DataSource, DbDriver, DbManager, DbOptions, DbProperty, DbRowEdit, DbTableKind, SortKey}; async fn setup() -> (tempfile::TempDir, DbManager) { let tmp = tempfile::tempdir().unwrap(); @@ -246,3 +246,23 @@ async fn ddl_of_tables_and_views() -> IdeResult<()> { assert!(db.ddl("s1", "main", "nope").await.is_err()); Ok(()) } + +#[tokio::test] +async fn applies_driver_properties_and_the_startup_script_on_connect() -> IdeResult<()> { + let (tmp, db) = setup().await; + let mut s = db.sources()?.remove(0); + s.properties = vec![DbProperty { name: "cache_size".into(), value: "-1234".into() }]; + s.options = DbOptions { + startup_script: "CREATE TEMP TABLE boot (x); INSERT INTO boot VALUES (7);".into(), + ..Default::default() + }; + db.save_sources(vec![s.clone()]).await?; + let c = CancellationToken::new(); + let r = db.execute("s1", "SELECT (SELECT x FROM boot), (SELECT cache_size FROM pragma_cache_size)", 10, &c).await?; + assert_eq!(r.rows, vec![vec![Some("7".into()), Some("-1234".into())]]); + // URL only: the file comes from the URL. + let url = format!("jdbc:sqlite:{}", tmp.path().join("app.db").display()); + db.save_sources(vec![DataSource { url_only: true, url, file: None, ..s }]).await?; + assert_eq!(db.schemas("s1").await?, vec!["main"]); + Ok(()) +} diff --git a/crates/ide-docker/Cargo.toml b/crates/ide-docker/Cargo.toml index e88de986..1431fa54 100644 --- a/crates/ide-docker/Cargo.toml +++ b/crates/ide-docker/Cargo.toml @@ -15,3 +15,6 @@ which = "8" [target.'cfg(unix)'.dependencies] libc = "0.2" + +[dev-dependencies] +tempfile.workspace = true diff --git a/crates/ide-docker/src/connection.rs b/crates/ide-docker/src/connection.rs new file mode 100644 index 00000000..2f78537e --- /dev/null +++ b/crates/ide-docker/src/connection.rs @@ -0,0 +1,402 @@ +//! Docker connections (which daemon the CLI talks to) and registries, as the +//! Services tool window's "Add Service" menu creates them. Both are app-wide +//! like in IDEA: `docker.json` in the app data directory; registry passwords +//! live beside it in an owner-only `docker-passwords.json`. + +use ide_core::{IdeError, IdeResult}; +use serde::{Deserialize, Serialize}; +use specta::Type; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::process::Command; + +/// Which daemon a docker command talks to. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(tag = "kind", rename_all = "camelCase", rename_all_fields = "camelCase")] +pub enum DockerEndpoint { + /// Whatever the CLI is configured for (`DOCKER_HOST`, the current context). + #[default] + Default, + /// A local daemon socket, e.g. `/var/run/docker.sock` (or a `npipe://` URL on Windows). + Socket { path: String }, + /// A remote daemon, e.g. `tcp://build:2376` or `ssh://me@build`; with TLS when + /// `tls_cert_dir` holds `ca.pem`, `cert.pem` and `key.pem`. + Tcp { host: String, tls_cert_dir: Option }, + /// A named `docker context`. + Context { name: String }, +} + +impl DockerEndpoint { + /// Global CLI options selecting this endpoint (before the subcommand). + pub fn global_args(&self) -> Vec { + match self { + Self::Default => Vec::new(), + Self::Socket { path } => { + let path = path.trim(); + let url = if path.contains("://") { path.to_owned() } else { format!("unix://{path}") }; + vec!["--host".into(), url] + } + Self::Tcp { host, tls_cert_dir } => { + let host = host.trim(); + let url = if host.contains("://") { host.to_owned() } else { format!("tcp://{host}") }; + let mut args = vec!["--host".into(), url]; + if let Some(dir) = tls_cert_dir.as_deref().map(str::trim).filter(|d| !d.is_empty()) { + let file = |name: &str| Path::new(dir).join(name).to_string_lossy().into_owned(); + args.extend([ + "--tlsverify".into(), + "--tlscacert".into(), + file("ca.pem"), + "--tlscert".into(), + file("cert.pem"), + "--tlskey".into(), + file("key.pem"), + ]); + } + args + } + Self::Context { name } => vec!["--context".into(), name.trim().to_owned()], + } + } + + /// Adds the global options to `cmd`; an explicit endpoint also drops the + /// inherited `DOCKER_HOST` / `DOCKER_CONTEXT`, which would conflict. + pub(crate) fn apply(&self, cmd: &mut Command) { + if *self != Self::Default { + cmd.env_remove("DOCKER_HOST").env_remove("DOCKER_CONTEXT").env_remove("DOCKER_TLS_VERIFY"); + } + cmd.args(self.global_args()); + } + + /// The same selection as environment variables, for docker command lines run by + /// others (run configurations). An empty value clears an inherited variable: the + /// CLI treats `DOCKER_HOST=""` as unset. + pub fn env_vars(&self) -> BTreeMap { + let mut env = BTreeMap::new(); + let mut set = |k: &str, v: String| { + env.insert(k.to_owned(), v); + }; + match self { + Self::Default => {} + Self::Socket { path } => { + let path = path.trim(); + set("DOCKER_HOST", if path.contains("://") { path.to_owned() } else { format!("unix://{path}") }); + set("DOCKER_CONTEXT", String::new()); + set("DOCKER_TLS_VERIFY", String::new()); + } + Self::Tcp { host, tls_cert_dir } => { + let host = host.trim(); + set("DOCKER_HOST", if host.contains("://") { host.to_owned() } else { format!("tcp://{host}") }); + set("DOCKER_CONTEXT", String::new()); + match tls_cert_dir.as_deref().map(str::trim).filter(|d| !d.is_empty()) { + // `DOCKER_CERT_PATH` holds `ca.pem`, `cert.pem` and `key.pem`, as `global_args` expects. + Some(dir) => { + set("DOCKER_TLS_VERIFY", "1".into()); + set("DOCKER_CERT_PATH", dir.to_owned()); + } + None => set("DOCKER_TLS_VERIFY", String::new()), + } + } + Self::Context { name } => { + set("DOCKER_CONTEXT", name.trim().to_owned()); + set("DOCKER_HOST", String::new()); + set("DOCKER_TLS_VERIFY", String::new()); + } + } + env + } + + pub fn validate(&self) -> IdeResult<()> { + let empty = match self { + Self::Default => false, + Self::Socket { path } => path.trim().is_empty(), + Self::Tcp { host, .. } => host.trim().is_empty(), + Self::Context { name } => name.trim().is_empty() || name.trim().starts_with('-'), + }; + if empty { Err(IdeError::invalid("the Docker connection needs a socket, host or context")) } else { Ok(()) } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct DockerConnection { + pub id: String, + pub name: String, + pub endpoint: DockerEndpoint, +} + +/// A registry to log in to (Docker Hub, GHCR, a private one). The password is stored separately. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct DockerRegistry { + pub id: String, + pub name: String, + /// `docker.io`, `ghcr.io`, `registry.example.com:5000`. + pub address: String, + #[serde(default)] + pub username: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct DockerSettings { + #[serde(default = "default_connections")] + pub connections: Vec, + #[serde(default)] + pub registries: Vec, +} + +impl Default for DockerSettings { + fn default() -> Self { + Self { connections: default_connections(), registries: Vec::new() } + } +} + +/// Before anything is configured the CLI's own daemon is shown, as before connections existed. +fn default_connections() -> Vec { + vec![DockerConnection { id: "default".into(), name: "Docker".into(), endpoint: DockerEndpoint::Default }] +} + +const SETTINGS_FILE: &str = "docker.json"; +const PASSWORDS_FILE: &str = "docker-passwords.json"; + +pub fn load_settings(data_dir: &Path) -> DockerSettings { + match std::fs::read(data_dir.join(SETTINGS_FILE)) { + Ok(bytes) => serde_json::from_slice(&bytes).unwrap_or_else(|e| { + tracing::warn!("{SETTINGS_FILE}: {e}"); + DockerSettings::default() + }), + Err(_) => DockerSettings::default(), + } +} + +pub fn save_settings(data_dir: &Path, settings: &DockerSettings) -> IdeResult<()> { + for c in &settings.connections { + if c.id.is_empty() || c.name.trim().is_empty() { + return Err(IdeError::invalid("every Docker connection needs a name")); + } + c.endpoint.validate()?; + } + for r in &settings.registries { + if r.id.is_empty() || r.address.trim().is_empty() { + return Err(IdeError::invalid("every Docker registry needs an address")); + } + } + let bytes = serde_json::to_vec_pretty(settings).map_err(|e| IdeError::internal(e.to_string()))?; + write_file(&data_dir.join(SETTINGS_FILE), &bytes, false)?; + // Forget passwords of registries that are gone. + let mut passwords = read_passwords(data_dir); + let before = passwords.len(); + passwords.retain(|id, _| settings.registries.iter().any(|r| &r.id == id)); + if passwords.len() != before { write_passwords(data_dir, &passwords) } else { Ok(()) } +} + +fn read_passwords(data_dir: &Path) -> BTreeMap { + std::fs::read(data_dir.join(PASSWORDS_FILE)).ok().and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default() +} + +fn write_passwords(data_dir: &Path, all: &BTreeMap) -> IdeResult<()> { + let bytes = serde_json::to_vec_pretty(all).map_err(|e| IdeError::internal(e.to_string()))?; + write_file(&data_dir.join(PASSWORDS_FILE), &bytes, true) +} + +pub fn registry_password(data_dir: &Path, id: &str) -> Option { + read_passwords(data_dir).remove(id) +} + +/// Saves (or with `None` forgets) the password of registry `id`. +pub fn set_registry_password(data_dir: &Path, id: &str, password: Option<&str>) -> IdeResult<()> { + let mut all = read_passwords(data_dir); + match password { + Some(p) => all.insert(id.to_owned(), p.to_owned()), + None => all.remove(id), + }; + write_passwords(data_dir, &all) +} + +fn write_file(path: &Path, bytes: &[u8], private: bool) -> IdeResult<()> { + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; + } + let tmp = path.with_extension("json.tmp"); + let mut options = std::fs::OpenOptions::new(); + options.write(true).create(true).truncate(true); + #[cfg(unix)] + if private { + std::os::unix::fs::OpenOptionsExt::mode(&mut options, 0o600); + } + #[cfg(not(unix))] + let _ = private; + std::io::Write::write_all(&mut options.open(&tmp)?, bytes)?; + std::fs::rename(&tmp, path)?; + Ok(()) +} + +/// Canonical registry host: no scheme or path; Docker Hub's aliases become `docker.io`. +pub fn registry_host(address: &str) -> String { + let a = address.trim(); + let a = a.split_once("://").map_or(a, |(_, rest)| rest); + let host = a.split('/').next().unwrap_or_default().to_ascii_lowercase(); + match host.as_str() { + "index.docker.io" | "registry-1.docker.io" | "registry.hub.docker.com" | "hub.docker.com" => "docker.io".into(), + _ => host, + } +} + +/// Registry host an image reference pulls from (`postgres:16` → `docker.io`). +pub fn reference_registry(reference: &str) -> String { + match reference.split_once('/') { + Some((first, _)) if first.contains('.') || first.contains(':') || first == "localhost" => registry_host(first), + _ => "docker.io".into(), + } +} + +/// Credentials for a `docker login`. +#[derive(Clone, Debug)] +pub struct RegistryAuth { + pub address: String, + pub username: String, + pub password: String, +} + +/// The saved registry (with a password) matching `reference`, if any. +pub fn auth_for(data_dir: &Path, reference: &str) -> Option { + let host = reference_registry(reference); + let settings = load_settings(data_dir); + let registry = + settings.registries.into_iter().find(|r| registry_host(&r.address) == host && !r.username.is_empty())?; + let password = registry_password(data_dir, ®istry.id)?; + Some(RegistryAuth { address: registry.address, username: registry.username, password }) +} + +/// A throwaway docker config directory: logins happen there, never in the user's +/// `~/.docker/config.json`. Contexts are copied so a context endpoint still resolves. +pub(crate) struct TempConfig { + pub dir: PathBuf, +} + +impl TempConfig { + pub fn new() -> IdeResult { + let nanos = + std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_nanos()).unwrap_or(0); + let dir = std::env::temp_dir().join(format!("openidea-docker-{}-{nanos}", std::process::id())); + std::fs::create_dir_all(&dir)?; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let _ = std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o700)); + } + if let Some(contexts) = user_config_dir().map(|d| d.join("contexts")).filter(|d| d.is_dir()) { + let _ = copy_dir(&contexts, &dir.join("contexts")); + } + Ok(Self { dir }) + } +} + +impl Drop for TempConfig { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.dir); + } +} + +fn user_config_dir() -> Option { + if let Some(dir) = std::env::var_os("DOCKER_CONFIG") { + return Some(PathBuf::from(dir)); + } + let home = std::env::var_os(if cfg!(windows) { "USERPROFILE" } else { "HOME" })?; + Some(PathBuf::from(home).join(".docker")) +} + +fn copy_dir(from: &Path, to: &Path) -> std::io::Result<()> { + std::fs::create_dir_all(to)?; + for e in std::fs::read_dir(from)?.flatten() { + let target = to.join(e.file_name()); + if e.file_type()?.is_dir() { + copy_dir(&e.path(), &target)?; + } else { + std::fs::copy(e.path(), target)?; + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn endpoint_args() { + assert!(DockerEndpoint::Default.global_args().is_empty()); + assert_eq!( + DockerEndpoint::Socket { path: "/var/run/docker.sock".into() }.global_args(), + ["--host", "unix:///var/run/docker.sock"] + ); + let tls = DockerEndpoint::Tcp { host: "build:2376".into(), tls_cert_dir: Some("/certs".into()) }.global_args(); + assert_eq!(tls[..3], ["--host", "tcp://build:2376", "--tlsverify"]); + assert!(tls.contains(&Path::new("/certs").join("key.pem").to_string_lossy().into_owned())); + assert_eq!( + DockerEndpoint::Tcp { host: "ssh://me@box".into(), tls_cert_dir: Some(" ".into()) }.global_args(), + ["--host", "ssh://me@box"] + ); + assert_eq!(DockerEndpoint::Context { name: "orbstack".into() }.global_args(), ["--context", "orbstack"]); + assert!(DockerEndpoint::Context { name: "--x".into() }.validate().is_err()); + } + + #[test] + fn endpoint_env() { + assert!(DockerEndpoint::Default.env_vars().is_empty()); + let socket = DockerEndpoint::Socket { path: "/var/run/docker.sock".into() }.env_vars(); + assert_eq!(socket["DOCKER_HOST"], "unix:///var/run/docker.sock"); + assert_eq!(socket["DOCKER_CONTEXT"], ""); + let tls = DockerEndpoint::Tcp { host: "build:2376".into(), tls_cert_dir: Some("/certs".into()) }.env_vars(); + assert_eq!( + (tls["DOCKER_HOST"].as_str(), tls["DOCKER_TLS_VERIFY"].as_str(), tls["DOCKER_CERT_PATH"].as_str()), + ("tcp://build:2376", "1", "/certs") + ); + let ssh = DockerEndpoint::Tcp { host: "ssh://me@box".into(), tls_cert_dir: None }.env_vars(); + assert_eq!((ssh["DOCKER_HOST"].as_str(), ssh["DOCKER_TLS_VERIFY"].as_str()), ("ssh://me@box", "")); + assert!(!ssh.contains_key("DOCKER_CERT_PATH")); + let ctx = DockerEndpoint::Context { name: " orbstack ".into() }.env_vars(); + assert_eq!((ctx["DOCKER_CONTEXT"].as_str(), ctx["DOCKER_HOST"].as_str()), ("orbstack", "")); + } + + #[test] + fn registries_match_references() { + assert_eq!(reference_registry("postgres:16"), "docker.io"); + assert_eq!(reference_registry("library/postgres"), "docker.io"); + assert_eq!(reference_registry("ghcr.io/org/app:1"), "ghcr.io"); + assert_eq!(reference_registry("localhost:5000/app"), "localhost:5000"); + assert_eq!(registry_host("https://index.docker.io/v1/"), "docker.io"); + assert_eq!(registry_host("GHCR.io"), "ghcr.io"); + } + + #[test] + fn settings_round_trip_and_passwords() { + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path(); + assert_eq!(load_settings(dir).connections[0].endpoint, DockerEndpoint::Default); + let settings = DockerSettings { + connections: vec![DockerConnection { + id: "c1".into(), + name: "Build box".into(), + endpoint: DockerEndpoint::Tcp { host: "build:2376".into(), tls_cert_dir: None }, + }], + registries: vec![DockerRegistry { + id: "r1".into(), + name: "GHCR".into(), + address: "ghcr.io".into(), + username: "me".into(), + }], + }; + save_settings(dir, &settings).unwrap(); + assert_eq!(load_settings(dir), settings); + set_registry_password(dir, "r1", Some("s3cret")).unwrap(); + let auth = auth_for(dir, "ghcr.io/org/app").unwrap(); + assert_eq!((auth.username.as_str(), auth.password.as_str()), ("me", "s3cret")); + assert!(auth_for(dir, "postgres").is_none()); + save_settings(dir, &DockerSettings { registries: Vec::new(), ..settings }).unwrap(); + assert_eq!(registry_password(dir, "r1"), None); + // An empty list stays empty (the default connection is only for a missing file). + save_settings(dir, &DockerSettings { connections: Vec::new(), registries: Vec::new() }).unwrap(); + assert!(load_settings(dir).connections.is_empty()); + } +} diff --git a/crates/ide-docker/src/lib.rs b/crates/ide-docker/src/lib.rs index 63c6d359..22d4549a 100644 --- a/crates/ide-docker/src/lib.rs +++ b/crates/ide-docker/src/lib.rs @@ -4,9 +4,11 @@ //! //! Every function blocks; callers run them off the UI / command threads. +mod connection; mod model; mod process; +pub use connection::*; pub use model::*; use ide_core::{CancellationToken, IdeError, IdeResult}; @@ -61,21 +63,38 @@ pub fn docker_executable() -> Option { .clone() } +/// Environment for docker command lines a run configuration launches: the endpoint's +/// variables plus a `PATH` that starts with docker's directory (GUI apps on macOS +/// lack the usual install locations), so `docker` / `docker compose` resolve. +pub fn run_env(ep: &DockerEndpoint) -> std::collections::BTreeMap { + let mut env = ep.env_vars(); + if let Some(dir) = docker_executable().as_deref().and_then(Path::parent) { + let current = std::env::var_os("PATH").unwrap_or_default(); + let mut dirs: Vec = vec![dir.to_path_buf()]; + dirs.extend(std::env::split_paths(¤t).filter(|d| d != dir)); + if let Ok(path) = std::env::join_paths(dirs) { + env.insert("PATH".into(), path.to_string_lossy().into_owned()); + } + } + env +} + fn binary() -> IdeResult { docker_executable() .ok_or_else(|| IdeError::unavailable("docker executable not found; install Docker and make sure it is on PATH")) } -fn command(args: &[&str]) -> IdeResult { +fn command(ep: &DockerEndpoint, args: &[&str]) -> IdeResult { let mut cmd = Command::new(binary()?); + ep.apply(&mut cmd); cmd.args(args).env("DOCKER_CLI_HINTS", "false").env("NO_COLOR", "1"); tracing::debug!(?args, "docker"); Ok(cmd) } /// Runs a docker command, mapping a non-zero exit to an error with docker's message. -fn run(args: &[&str], timeout: Duration) -> IdeResult { - let out = process::run(command(args)?, timeout, None)?; +fn run(ep: &DockerEndpoint, args: &[&str], timeout: Duration) -> IdeResult { + let out = process::run(command(ep, args)?, timeout, None)?; if out.success { Ok(out.stdout) } else { Err(failure(args, &out.stderr, &out.stdout)) } } @@ -94,8 +113,8 @@ fn failure(args: &[&str], stderr: &str, stdout: &str) -> IdeError { } /// Whether docker can be used right now. -pub fn availability() -> DockerAvailability { - let Ok(cmd) = command(&["version", "--format", "{{json .}}"]) else { +pub fn availability(ep: &DockerEndpoint) -> DockerAvailability { + let Ok(cmd) = command(ep, &["version", "--format", "{{json .}}"]) else { return DockerAvailability::NotInstalled; }; match process::run(cmd, Duration::from_secs(15), None) { @@ -105,8 +124,8 @@ pub fn availability() -> DockerAvailability { } /// Availability plus containers, images, volumes and networks (listed in parallel). -pub fn snapshot() -> DockerSnapshot { - let availability = availability(); +pub fn snapshot(ep: &DockerEndpoint) -> DockerSnapshot { + let availability = availability(ep); let executable = docker_executable().map(|p| ide_core::ipc_path::to_ipc(&p)); let mut snap = DockerSnapshot { availability, @@ -116,16 +135,17 @@ pub fn snapshot() -> DockerSnapshot { volumes: Vec::new(), networks: Vec::new(), errors: Vec::new(), + global_args: ep.global_args(), }; if !matches!(snap.availability, DockerAvailability::Ready { .. }) { return snap; } const FMT: &str = "{{json .}}"; let (containers, images, volumes, networks) = std::thread::scope(|s| { - let c = s.spawn(|| run(&["ps", "--all", "--no-trunc", "--format", FMT], QUICK)); - let i = s.spawn(|| run(&["image", "ls", "--no-trunc", "--format", FMT], QUICK)); - let v = s.spawn(|| run(&["volume", "ls", "--format", FMT], QUICK)); - let n = s.spawn(|| run(&["network", "ls", "--no-trunc", "--format", FMT], QUICK)); + let c = s.spawn(|| run(ep, &["ps", "--all", "--no-trunc", "--format", FMT], QUICK)); + let i = s.spawn(|| run(ep, &["image", "ls", "--no-trunc", "--format", FMT], QUICK)); + let v = s.spawn(|| run(ep, &["volume", "ls", "--format", FMT], QUICK)); + let n = s.spawn(|| run(ep, &["network", "ls", "--no-trunc", "--format", FMT], QUICK)); let join = |h: std::thread::ScopedJoinHandle<'_, IdeResult>| { h.join().unwrap_or_else(|_| Err(IdeError::internal("docker listing panicked"))) }; @@ -178,13 +198,13 @@ impl ContainerOp { } /// Starts / stops / … the containers `ids`. -pub fn container_op(ids: &[String], op: ContainerOp) -> IdeResult<()> { +pub fn container_op(ep: &DockerEndpoint, ids: &[String], op: ContainerOp) -> IdeResult<()> { if ids.is_empty() { return Ok(()); } let mut args = vec!["container", op.verb()]; args.extend(ids.iter().map(String::as_str)); - run(&args, LIFECYCLE).map(drop) + run(ep, &args, LIFECYCLE).map(drop) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -208,7 +228,7 @@ impl DockerObject { } /// `docker rm` (`force`: stop running containers / untag used images first). -pub fn remove(object: DockerObject, ids: &[String], force: bool) -> IdeResult<()> { +pub fn remove(ep: &DockerEndpoint, object: DockerObject, ids: &[String], force: bool) -> IdeResult<()> { if ids.is_empty() { return Ok(()); } @@ -217,12 +237,12 @@ pub fn remove(object: DockerObject, ids: &[String], force: bool) -> IdeResult<() args.push("--force"); } args.extend(ids.iter().map(String::as_str)); - run(&args, LIFECYCLE).map(drop) + run(ep, &args, LIFECYCLE).map(drop) } /// `docker inspect` of one object, pretty-printed. -pub fn inspect(object: DockerObject, id: &str) -> IdeResult { - let out = run(&["inspect", "--type", object.noun(), id], QUICK)?; +pub fn inspect(ep: &DockerEndpoint, object: DockerObject, id: &str) -> IdeResult { + let out = run(ep, &["inspect", "--type", object.noun(), id], QUICK)?; match serde_json::from_str::(&out) { // A one-element array for a single id: show the object itself. Ok(serde_json::Value::Array(mut items)) if items.len() == 1 => { @@ -236,6 +256,7 @@ pub fn inspect(object: DockerObject, id: &str) -> IdeResult { /// Streams a container's logs (`tail` last lines, then follows while `follow`) /// until it ends or `cancel` fires. pub fn logs( + ep: &DockerEndpoint, id: &str, tail: u32, follow: bool, @@ -252,16 +273,16 @@ pub fn logs( args.push("--timestamps"); } args.push(id); - let (ok, stderr) = process::stream(command(&args)?, cancel, &mut |stream, text| { + let (ok, stderr) = process::stream(command(ep, &args)?, cancel, &mut |stream, text| { on_line(DockerLogLine { stderr: stream == Stream::Stderr, text }) })?; if ok { Ok(()) } else { Err(failure(&args, &stderr.join("\n"), "")) } } /// Follows `docker events` until it ends (daemon gone) or `cancel` fires. -pub fn watch_events(cancel: &CancellationToken, on_event: &dyn Fn(DockerEvent)) -> IdeResult<()> { +pub fn watch_events(ep: &DockerEndpoint, cancel: &CancellationToken, on_event: &dyn Fn(DockerEvent)) -> IdeResult<()> { let args = ["events", "--format", "{{json .}}"]; - let (ok, stderr) = process::stream(command(&args)?, cancel, &mut |stream, line| { + let (ok, stderr) = process::stream(command(ep, &args)?, cancel, &mut |stream, line| { if stream == Stream::Stdout && let Some(e) = parse_event(&line) { @@ -272,9 +293,19 @@ pub fn watch_events(cancel: &CancellationToken, on_event: &dyn Fn(DockerEvent)) } /// Runs a long docker command (pull, prune, compose…), streaming its output lines. -fn run_streaming(args: &[&str], cancel: &CancellationToken, on_output: &dyn Fn(String)) -> IdeResult<()> { +fn run_streaming( + ep: &DockerEndpoint, + config: Option<&Path>, + args: &[&str], + cancel: &CancellationToken, + on_output: &dyn Fn(String), +) -> IdeResult<()> { on_output(format!("docker {}", args.join(" "))); - let (ok, stderr) = process::stream(command(args)?, cancel, &mut |_, line| { + let mut cmd = command(ep, args)?; + if let Some(dir) = config { + cmd.env("DOCKER_CONFIG", dir); + } + let (ok, stderr) = process::stream(cmd, cancel, &mut |_, line| { if !line.trim().is_empty() { on_output(line); } @@ -282,13 +313,57 @@ fn run_streaming(args: &[&str], cancel: &CancellationToken, on_output: &dyn Fn(S if ok { Ok(()) } else { Err(failure(args, &stderr.join("\n"), "")) } } -/// `docker pull `. -pub fn pull(reference: &str, cancel: &CancellationToken, on_output: &dyn Fn(String)) -> IdeResult<()> { +/// `docker pull `; with `auth` it logs in to the registry first, in a +/// throwaway config directory (the user's docker config stays untouched). +pub fn pull( + ep: &DockerEndpoint, + reference: &str, + auth: Option<&RegistryAuth>, + cancel: &CancellationToken, + on_output: &dyn Fn(String), +) -> IdeResult<()> { let reference = reference.trim(); if reference.is_empty() || reference.starts_with('-') { return Err(IdeError::invalid("enter an image name, e.g. postgres:16")); } - run_streaming(&["pull", reference], cancel, on_output) + let Some(auth) = auth else { return run_streaming(ep, None, &["pull", reference], cancel, on_output) }; + let config = TempConfig::new()?; + on_output(format!("Logging in to {} as {}", auth.address, auth.username)); + login_in(ep, &config.dir, auth, Some(cancel))?; + run_streaming(ep, Some(&config.dir), &["pull", reference], cancel, on_output) +} + +/// `docker login` with `auth`; returns docker's answer (`Login Succeeded`). Nothing is +/// stored in the user's docker config. +pub fn login(ep: &DockerEndpoint, auth: &RegistryAuth) -> IdeResult { + let config = TempConfig::new()?; + login_in(ep, &config.dir, auth, None) +} + +fn login_in( + ep: &DockerEndpoint, + config: &Path, + auth: &RegistryAuth, + cancel: Option<&CancellationToken>, +) -> IdeResult { + let host = registry_host(&auth.address); + let user = auth.username.trim(); + if host.is_empty() || host.starts_with('-') || user.is_empty() || user.starts_with('-') { + return Err(IdeError::invalid("the registry needs an address and a username")); + } + let mut args = vec!["login", "--username", user, "--password-stdin"]; + // Docker Hub is the CLI's default server. + if host != "docker.io" { + args.push(&host); + } + let mut cmd = command(ep, &args)?; + cmd.env("DOCKER_CONFIG", config); + let out = process::run_with_input(cmd, QUICK, cancel, Some(&auth.password))?; + if out.success { + Ok(out.stdout.lines().map(str::trim).rfind(|l| !l.is_empty()).unwrap_or("Login Succeeded").to_owned()) + } else { + Err(failure(&args, &out.stderr, &out.stdout)) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -306,7 +381,12 @@ pub enum PruneTarget { } /// `docker prune --force`. -pub fn prune(target: PruneTarget, cancel: &CancellationToken, on_output: &dyn Fn(String)) -> IdeResult<()> { +pub fn prune( + ep: &DockerEndpoint, + target: PruneTarget, + cancel: &CancellationToken, + on_output: &dyn Fn(String), +) -> IdeResult<()> { let args: &[&str] = match target { PruneTarget::Containers => &["container", "prune", "--force"], PruneTarget::Images => &["image", "prune", "--force"], @@ -315,7 +395,7 @@ pub fn prune(target: PruneTarget, cancel: &CancellationToken, on_output: &dyn Fn PruneTarget::Networks => &["network", "prune", "--force"], PruneTarget::System => &["system", "prune", "--force"], }; - run_streaming(args, cancel, on_output) + run_streaming(ep, None, args, cancel, on_output) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -332,6 +412,7 @@ pub enum ComposeOp { /// labels. `files` / `working_dir` (from the labels too) are passed when they still /// exist, so commands that need the model keep working. pub fn compose_op( + ep: &DockerEndpoint, project: &str, files: &[String], working_dir: Option<&str>, @@ -357,5 +438,5 @@ pub fn compose_op( ComposeOp::Restart => "restart", ComposeOp::Down => "down", }); - run_streaming(&args, cancel, on_output) + run_streaming(ep, None, &args, cancel, on_output) } diff --git a/crates/ide-docker/src/model.rs b/crates/ide-docker/src/model.rs index d803ef7a..1cdc1098 100644 --- a/crates/ide-docker/src/model.rs +++ b/crates/ide-docker/src/model.rs @@ -105,6 +105,9 @@ pub struct DockerSnapshot { pub networks: Vec, /// Lists that failed although the daemon answered (shown, not fatal). pub errors: Vec, + /// Global CLI options selecting the connection's daemon (for `docker exec` in a terminal). + #[serde(default)] + pub global_args: Vec, } /// One relevant `docker events` entry. diff --git a/crates/ide-docker/src/process.rs b/crates/ide-docker/src/process.rs index f0c47f04..5deb05e3 100644 --- a/crates/ide-docker/src/process.rs +++ b/crates/ide-docker/src/process.rs @@ -60,9 +60,26 @@ fn read_all(mut pipe: impl Read + Send + 'static) -> std::thread::JoinHandle) -> IdeResult { +pub(crate) fn run(cmd: Command, timeout: Duration, cancel: Option<&CancellationToken>) -> IdeResult { + run_with_input(cmd, timeout, cancel, None) +} + +/// [`run`] with `input` written to the child's stdin (e.g. `--password-stdin`). +pub(crate) fn run_with_input( + mut cmd: Command, + timeout: Duration, + cancel: Option<&CancellationToken>, + input: Option<&str>, +) -> IdeResult { prepare(&mut cmd); + if input.is_some() { + cmd.stdin(Stdio::piped()); + } let mut child = spawn(&mut cmd)?; + if let (Some(text), Some(mut stdin)) = (input, child.stdin.take()) { + // Dropping `stdin` afterwards closes it, so the CLI sees EOF. + let _ = std::io::Write::write_all(&mut stdin, text.as_bytes()); + } let out = child.stdout.take().map(read_all); let err = child.stderr.take().map(read_all); let deadline = Instant::now() + timeout; diff --git a/crates/ide-index/src/data.rs b/crates/ide-index/src/data.rs index 4dddf826..59115430 100644 --- a/crates/ide-index/src/data.rs +++ b/crates/ide-index/src/data.rs @@ -181,10 +181,10 @@ impl IndexData { Some(old) } - /// Removes every file at or below `dir`; returns their relative paths. - pub(crate) fn remove_tree(&mut self, dir: &Path) -> Vec { + /// Removes every file at or below `dir`; returns their paths. + pub(crate) fn remove_tree(&mut self, dir: &Path) -> Vec { let doomed: Vec = self.by_path.keys().filter(|p| p.starts_with(dir)).cloned().collect(); - doomed.iter().filter_map(|p| self.remove(p)).map(|e| e.rel.into_string()).collect() + doomed.into_iter().filter(|p| self.remove(p).is_some()).collect() } /// Releases spare capacity (after large deletions). @@ -235,7 +235,7 @@ mod tests { assert_eq!((d.file_count, d.symbol_count), (1, 5)); d.insert(entry("d.rs", 0)); assert_eq!(d.files.len(), 2, "slot reused"); - assert_eq!(d.remove_tree(Path::new("/r/a")), vec!["a/b.rs".to_owned()]); + assert_eq!(d.remove_tree(Path::new("/r/a")), vec![PathBuf::from("/r/a/b.rs")]); assert_eq!((d.file_count, d.symbol_count), (1, 0)); let e = d.get(Path::new("/r/d.rs")).unwrap(); assert_eq!((e.name(), e.dir()), ("d.rs", "")); diff --git a/crates/ide-index/src/filter.rs b/crates/ide-index/src/filter.rs index edf5b5a3..eb875610 100644 --- a/crates/ide-index/src/filter.rs +++ b/crates/ide-index/src/filter.rs @@ -1,5 +1,6 @@ //! Which paths belong in the index: `.gitignore`/`.ignore` rules (via the -//! `ignore` crate) plus the user's `files.exclude` globs. +//! `ignore` crate) plus the user's `files.exclude` globs, below the project root +//! and the extra content roots (Project Structure). use globset::{GlobBuilder, GlobSet, GlobSetBuilder}; use ignore::gitignore::{Gitignore, GitignoreBuilder}; @@ -14,10 +15,45 @@ const ALWAYS_SKIP: &[&str] = &[".git", ".hg", ".svn", ".jj"]; pub(crate) struct PathFilter { root: PathBuf, + /// Content roots outside `root`, indexed as `/`. + extra: Vec, excludes: GlobSet, /// Root-level ignore files, for paths reported by the watcher (the walker /// applies nested ignore files itself). gitignore: Gitignore, + /// What the filter was built from: `(extra roots, exclude globs)`. + config: (Vec, Vec), +} + +struct ExtraRoot { + path: PathBuf, + /// Shown before the paths inside it (`shared-lib/src/x.rs`). + name: String, + gitignore: Gitignore, +} + +fn root_ignores(root: &Path) -> Gitignore { + let mut gi = GitignoreBuilder::new(root); + for f in [".gitignore", ".ignore", ".git/info/exclude"] { + let path = root.join(f); + if path.is_file() + && let Some(e) = gi.add(&path) + { + tracing::debug!("{}: {e}", path.display()); + } + } + gi.build().unwrap_or_else(|_| Gitignore::empty()) +} + +fn rel_string(rel: &Path) -> String { + let mut out = String::new(); + for c in rel.components() { + if !out.is_empty() { + out.push('/'); + } + out.push_str(&c.as_os_str().to_string_lossy()); + } + out } #[derive(Clone, Debug)] @@ -32,9 +68,9 @@ pub(crate) fn mtime_of(meta: &std::fs::Metadata) -> u64 { } impl PathFilter { - pub(crate) fn new(root: &Path, excludes: &[String]) -> Self { + pub(crate) fn new(root: &Path, extra_roots: &[PathBuf], exclude_globs: &[String]) -> Self { let mut set = GlobSetBuilder::new(); - for raw in excludes { + for raw in exclude_globs { let p = raw.trim().trim_end_matches('/'); let anchored = p.starts_with('/'); let p = p.trim_start_matches("./").trim_start_matches('/'); @@ -55,34 +91,76 @@ impl PathFilter { } } let excludes = set.build().unwrap_or_else(|_| GlobSet::empty()); - let mut gi = GitignoreBuilder::new(root); - for f in [".gitignore", ".ignore", ".git/info/exclude"] { - let path = root.join(f); - if path.is_file() - && let Some(e) = gi.add(&path) - { - tracing::debug!("{}: {e}", path.display()); + let mut extra: Vec = Vec::new(); + for r in extra_roots { + let path = dunce::canonicalize(r).unwrap_or_else(|_| r.clone()); + let nested = path.starts_with(root) + || root.starts_with(&path) + || extra.iter().any(|e| path.starts_with(&e.path) || e.path.starts_with(&path)); + if nested || !path.is_dir() { + continue; } + let name = path.file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_default(); + extra.push(ExtraRoot { gitignore: root_ignores(&path), name, path }); } - let gitignore = gi.build().unwrap_or_else(|_| Gitignore::empty()); - Self { root: root.to_owned(), excludes, gitignore } + Self { + root: root.to_owned(), + extra, + excludes, + gitignore: root_ignores(root), + config: (extra_roots.to_vec(), exclude_globs.to_vec()), + } + } + + /// Whether the filter was built from these roots and globs. + pub(crate) fn same_config(&self, extra_roots: &[PathBuf], excludes: &[String]) -> bool { + self.config.0 == extra_roots && self.config.1 == excludes } pub(crate) fn root(&self) -> &Path { &self.root } - /// `/`-separated path relative to the root. - pub(crate) fn rel(&self, path: &Path) -> Option { - let rel = path.strip_prefix(&self.root).ok()?; - let mut out = String::new(); - for c in rel.components() { - if !out.is_empty() { - out.push('/'); - } - out.push_str(&c.as_os_str().to_string_lossy()); + /// The project root, then the extra content roots. + pub(crate) fn roots(&self) -> impl Iterator { + std::iter::once(self.root.as_path()).chain(self.extra.iter().map(|e| e.path.as_path())) + } + + /// The root containing `path`, its display prefix (`None` for the project root) and ignore rules. + fn base_of(&self, path: &Path) -> Option<(&Path, Option<&str>, &Gitignore)> { + if path.starts_with(&self.root) { + return Some((&self.root, None, &self.gitignore)); } - (!out.is_empty()).then_some(out) + self.extra + .iter() + .find(|e| path.starts_with(&e.path)) + .map(|e| (e.path.as_path(), Some(e.name.as_str()), &e.gitignore)) + } + + /// Whether `path` is (at or) below one of the roots. + pub(crate) fn contains(&self, path: &Path) -> bool { + self.base_of(path).is_some() + } + + /// `/`-separated path relative to its root, as displayed; paths in extra + /// content roots start with the root's folder name. + pub(crate) fn rel(&self, path: &Path) -> Option { + let (base, prefix, _) = self.base_of(path)?; + let rel = rel_string(path.strip_prefix(base).ok()?); + if rel.is_empty() { + return None; + } + Some(match prefix { + Some(p) => format!("{p}/{rel}"), + None => rel, + }) + } + + /// Key of `path` in the persistent store: only files below the project root + /// are persisted (extra content roots are rescanned when the project opens). + pub(crate) fn store_key(&self, path: &Path) -> Option { + let rel = rel_string(path.strip_prefix(&self.root).ok()?); + (!rel.is_empty()).then_some(rel) } fn excluded_rel(&self, rel: &str) -> bool { @@ -96,7 +174,11 @@ impl PathFilter { /// Whether a path reported by the watcher should be ignored (outside the /// root, inside an excluded/ignored directory, or ignored itself). pub(crate) fn is_excluded(&self, path: &Path, is_dir: bool) -> bool { - let Some(rel) = self.rel(path) else { return true }; + let Some((base, _, gitignore)) = self.base_of(path) else { return true }; + let rel = path.strip_prefix(base).map(rel_string).unwrap_or_default(); + if rel.is_empty() { + return true; + } let mut prefix = String::new(); for seg in rel.split('/') { if Self::skip_name(seg) { @@ -110,14 +192,15 @@ impl PathFilter { return true; } } - self.gitignore.matched_path_or_any_parents(path, is_dir).is_ignore() + gitignore.matched_path_or_any_parents(path, is_dir).is_ignore() } /// All indexable files at or below `dir`, walked in parallel. /// Stops early (returning what was found so far) once `cancel` is set. pub(crate) fn walk(&self, dir: &Path, cancel: &AtomicBool) -> Vec { let (tx, rx) = mpsc::channel::>(); - let root = self.root.clone(); + let Some((root, _, _)) = self.base_of(dir) else { return Vec::new() }; + let root = root.to_path_buf(); let excludes = self.excludes.clone(); let walker = WalkBuilder::new(dir) .hidden(false) @@ -207,7 +290,7 @@ mod tests { touch(&root, f); } std::fs::write(root.join(".gitignore"), "build/\n").unwrap(); - let filter = PathFilter::new(&root, &["**/node_modules".into(), "target".into(), "docs/**".into()]); + let filter = PathFilter::new(&root, &[], &["**/node_modules".into(), "target".into(), "docs/**".into()]); let mut found: Vec = filter.walk(&root, &AtomicBool::new(false)).iter().filter_map(|f| filter.rel(&f.path)).collect(); found.sort(); @@ -220,4 +303,31 @@ mod tests { assert!(filter.is_excluded(Path::new("/elsewhere/x.rs"), false)); assert!(!filter.is_excluded(&root.join("src/new.rs"), false)); } + + #[test] + fn extra_content_roots_are_walked_and_prefixed() { + let dir = tempfile::tempdir().unwrap(); + let base = dunce::canonicalize(dir.path()).unwrap(); + let (root, shared) = (base.join("app"), base.join("shared-lib")); + for f in ["app/src/a.rs", "shared-lib/src/b.rs", "shared-lib/target/gen.rs", "shared-lib/out/x.rs"] { + touch(&base, f); + } + std::fs::write(shared.join(".gitignore"), "out/\n").unwrap(); + let filter = PathFilter::new(&root, &[shared.clone(), root.join("src")], &["target".into()]); + assert_eq!(filter.roots().count(), 2, "roots inside the project are not extra roots"); + let mut found: Vec = filter + .roots() + .flat_map(|r| filter.walk(r, &AtomicBool::new(false))) + .filter_map(|f| filter.rel(&f.path)) + .collect(); + found.sort(); + assert_eq!(found, ["shared-lib/.gitignore", "shared-lib/src/b.rs", "src/a.rs"]); + assert_eq!(filter.store_key(&shared.join("src/b.rs")), None, "extra roots are not persisted"); + assert_eq!(filter.store_key(&root.join("src/a.rs")).as_deref(), Some("src/a.rs")); + assert!(!filter.is_excluded(&shared.join("src/new.rs"), false)); + assert!(filter.is_excluded(&shared.join("target/y.rs"), false)); + assert!(filter.is_excluded(&shared.join("out/y.rs"), false)); + assert!(filter.same_config(&[shared.clone(), root.join("src")], &["target".into()])); + assert!(!filter.same_config(&[shared], &["target".into()])); + } } diff --git a/crates/ide-index/src/lib.rs b/crates/ide-index/src/lib.rs index 2d737f9f..ac7361c2 100644 --- a/crates/ide-index/src/lib.rs +++ b/crates/ide-index/src/lib.rs @@ -67,14 +67,17 @@ pub struct IndexStatus { pub struct ProjectIndex { shared: Arc, worker: Mutex>>, + root: PathBuf, } impl ProjectIndex { /// Opens (or creates) the persistent index at `cache_dir` and starts a /// background full scan that skips files whose content hash is unchanged. + /// `content_roots` are folders outside `root` indexed with it (not persisted). pub fn open( root: &Path, cache_dir: &Path, + content_roots: Vec, excludes: Vec, languages: Arc, syntax: Arc, @@ -85,7 +88,7 @@ impl ProjectIndex { return Err(ide_core::IdeError::not_found(format!("{} is not a directory", root.display()))); } let store = store::Store::open(cache_dir, &root, &syntax.fingerprint()); - let filter = filter::PathFilter::new(&root, &excludes); + let filter = filter::PathFilter::new(&root, &content_roots, &excludes); let shared = Arc::new(Shared::new(filter, languages, syntax, sink, store)); let worker = { let shared = shared.clone(); @@ -94,7 +97,25 @@ impl ProjectIndex { .spawn(move || shared.run()) .map_err(|e| ide_core::IdeError::internal(format!("cannot start indexer: {e}")))? }; - Ok(Arc::new(Self { shared, worker: Mutex::new(Some(worker)) })) + Ok(Arc::new(Self { shared, worker: Mutex::new(Some(worker)), root })) + } + + /// Changes what is indexed besides the root (content roots) and the exclude + /// globs, rescanning in the background (unchanged files are not re-read). + /// Returns whether anything changed. + pub fn set_scope(&self, content_roots: Vec, excludes: Vec) -> bool { + if self.shared.filter().same_config(&content_roots, &excludes) { + return false; + } + self.shared.set_filter(filter::PathFilter::new(&self.root, &content_roots, &excludes)); + self.shared.enqueue(Job::Rescan); + true + } + + /// File types changed (the registry the index shares was updated): re-parses + /// the files whose language under `before` differs from the current one. + pub fn languages_changed(&self, before: Arc) { + self.shared.enqueue(Job::Reparse(before)); } pub fn status(&self) -> IndexStatus { @@ -202,14 +223,15 @@ impl ProjectIndex { } pub fn root(&self) -> &Path { - self.shared.filter.root() + &self.root } - /// Absolute path inside the root, or `None` for paths outside the project / excluded. + /// Absolute path inside the root (or a content root), or `None` for paths outside the project. fn normalize(&self, path: &Path) -> Option { - let root = self.shared.filter.root(); + let filter = self.shared.filter(); + let root = &self.root; let path = if path.is_absolute() { path.to_owned() } else { root.join(path) }; - let path = if path.starts_with(root) { + let path = if filter.contains(&path) { path } else { // Watchers / the UI may report non-canonical spellings (`/tmp` vs `/private/tmp`). @@ -221,7 +243,7 @@ impl ProjectIndex { } } }; - (path.starts_with(root) && path != root).then_some(path) + (filter.contains(&path) && filter.rel(&path).is_some()).then_some(path) } } diff --git a/crates/ide-index/src/tests.rs b/crates/ide-index/src/tests.rs index 1de6c22d..3d6bbdd9 100644 --- a/crates/ide-index/src/tests.rs +++ b/crates/ide-index/src/tests.rs @@ -37,6 +37,7 @@ impl Fixture { let idx = ProjectIndex::open( &self.root, &self.cache, + Vec::new(), vec!["**/node_modules".into(), "**/target".into()], languages, syntax, @@ -403,3 +404,72 @@ fn css_selectors_are_found_by_exact_name() { assert_eq!(idx.symbols_named("--gap", None, 10).len(), 1); assert!(idx.symbols_named(".card", Some(SymbolKind::Field), 10).is_empty()); } + +#[test] +fn content_roots_and_excludes_change_without_reopening() { + let fx = Fixture::new(&[("src/main.rs", "fn alpha() {}\n"), ("src/gen/out.rs", "fn generated() {}\n")]); + let shared = fx._dir.path().join("shared-lib"); + std::fs::create_dir_all(shared.join("src")).unwrap(); + std::fs::write(shared.join("src/lib.rs"), "fn gamma() {}\n").unwrap(); + let idx = fx.open(); + assert!(files(&idx, "lib.rs").is_empty()); + + let excludes = vec!["**/node_modules".to_owned(), "**/target".to_owned()]; + assert!(idx.set_scope(vec![shared.clone()], excludes.clone())); + assert!(!idx.set_scope(vec![shared.clone()], excludes.clone()), "unchanged scope"); + assert!(idx.wait_idle(WAIT)); + assert_eq!(files(&idx, "lib.rs"), ["shared-lib/src/lib.rs"]); + let gamma = &idx.find_symbols("gamma", SymbolFilter::All, 5)[0]; + assert_eq!(gamma.path, dunce::canonicalize(shared.join("src/lib.rs")).unwrap()); + + // Files created in a content root are picked up incrementally. + std::fs::write(shared.join("src/more.rs"), "fn delta() {}\n").unwrap(); + idx.update(&[shared.join("src/more.rs")], &[]); + assert!(idx.wait_idle(WAIT)); + assert_eq!(symbols(&idx, "delta", SymbolFilter::All), ["delta"]); + + // A new exclude drops what it matches; removing the root drops its files. + let mut more = excludes.clone(); + more.push("gen".into()); + idx.set_scope(Vec::new(), more); + assert!(idx.wait_idle(WAIT)); + assert!(files(&idx, "out.rs").is_empty()); + assert!(files(&idx, "lib.rs").is_empty()); + assert_eq!(idx.status().files, 1); + drop(idx); + + // Extra roots are not persisted: reopening without them lists only the project. + let idx = fx.open(); + assert!(symbols(&idx, "gamma", SymbolFilter::All).is_empty()); +} + +#[test] +fn file_type_changes_reparse_affected_files() { + let fx = Fixture::new(&[("src/main.rs", "fn alpha() {}\n"), ("src/legacy.inc", "fn beta() {}\n")]); + let languages = Arc::new(LanguageRegistry::load(None).unwrap()); + let idx = ProjectIndex::open( + &fx.root, + &fx.cache, + Vec::new(), + Vec::new(), + languages.clone(), + Arc::new(Syntax::new()), + Arc::new(NullSink), + ) + .unwrap(); + assert!(idx.wait_idle(WAIT)); + assert!(symbols(&idx, "beta", SymbolFilter::All).is_empty(), "plain text has no symbols"); + let parsed = idx.shared.parsed.load(Ordering::SeqCst); + + let before = Arc::new(languages.snapshot()); + let mut ft = ide_language::FileTypes::default(); + ft.associations.insert( + "rust".into(), + ide_core::settings::FileTypeAssociation { patterns: vec!["*.inc".into()], ..Default::default() }, + ); + assert!(languages.set_file_types(ft)); + idx.languages_changed(before); + assert!(idx.wait_idle(WAIT)); + assert_eq!(symbols(&idx, "beta", SymbolFilter::All), ["beta"]); + assert_eq!(idx.shared.parsed.load(Ordering::SeqCst), parsed + 1, "only the affected file is re-parsed"); +} diff --git a/crates/ide-index/src/worker.rs b/crates/ide-index/src/worker.rs index 1e25d4b5..e3fbd919 100644 --- a/crates/ide-index/src/worker.rs +++ b/crates/ide-index/src/worker.rs @@ -24,6 +24,11 @@ pub(crate) enum Job { Changed(Vec), Removed(Vec), SaveRecent, + /// The roots or excludes changed: scan again (unchanged files are kept). + Rescan, + /// File types changed: re-parse the indexed files whose language under the + /// previous registry differs from the current one. + Reparse(std::sync::Arc), } #[derive(Default)] @@ -34,7 +39,7 @@ struct Queue { } pub(crate) struct Shared { - pub filter: PathFilter, + filter: RwLock>, pub languages: std::sync::Arc, pub syntax: std::sync::Arc, pub sink: SharedSink, @@ -60,7 +65,7 @@ impl Shared { store: Option, ) -> Self { Self { - filter, + filter: RwLock::new(std::sync::Arc::new(filter)), languages, syntax, sink, @@ -76,6 +81,14 @@ impl Shared { } } + pub(crate) fn filter(&self) -> std::sync::Arc { + self.filter.read().clone() + } + + pub(crate) fn set_filter(&self, filter: PathFilter) { + *self.filter.write() = std::sync::Arc::new(filter); + } + pub(crate) fn enqueue(&self, job: Job) { self.queue.lock().jobs.push_back(job); self.wake.notify_one(); @@ -150,7 +163,7 @@ impl Shared { fn load_persisted(&self) { let Some(store) = &self.store else { return }; - let root = self.filter.root().to_owned(); + let root = self.filter().root().to_owned(); match store.load() { Ok(records) => { let entries: Vec = @@ -198,26 +211,31 @@ impl Shared { done: false, }); - let found = self.filter.walk(self.filter.root(), &self.cancelled); + let filter = self.filter(); + let mut found = Vec::new(); + for root in filter.roots() { + found.extend(filter.walk(root, &self.cancelled)); + } if self.is_cancelled() { return self.end_scan(task); } let seen: HashSet<&Path> = found.iter().map(|f| f.path.as_path()).collect(); - let (todo, stale): (Vec<&Found>, Vec) = { + let (todo, stale): (Vec<&Found>, Vec) = { let d = self.data.read(); let todo = found .iter() .filter(|f| d.get(&f.path).is_none_or(|e| e.mtime != f.mtime || e.size != f.size)) .collect(); - let stale = d.paths().filter(|p| !seen.contains(p.as_path())).filter_map(|p| self.filter.rel(p)).collect(); + let stale = d.paths().filter(|p| !seen.contains(p.as_path())).cloned().collect(); (todo, stale) }; if !stale.is_empty() { let mut d = self.data.write(); - for rel in &stale { - d.remove(&self.filter.root().join(rel)); + for path in &stale { + d.remove(path); } } + let stale: Vec = stale.iter().filter_map(|p| filter.store_key(p)).collect(); self.persist(&[], &stale, false); let total = todo.len(); @@ -227,7 +245,7 @@ impl Shared { if self.is_cancelled() { return self.end_scan(task); } - self.index_files(chunk.iter().copied()); + self.index_files(chunk.iter().copied(), false); done += chunk.len(); if last_progress.elapsed() >= PROGRESS_EVERY { last_progress = Instant::now(); @@ -261,16 +279,18 @@ impl Shared { } /// Extracts records in parallel, then publishes them to memory and disk. - fn index_files<'a>(&self, files: impl Iterator) { + /// `force` re-parses files whose content is unchanged (their language changed). + fn index_files<'a>(&self, files: impl Iterator, force: bool) { let files: Vec<&Found> = files.collect(); + let filter = self.filter(); let extracted: Vec<(String, FileRecord, PathBuf)> = files .par_iter() .filter_map(|f| { if self.is_cancelled() { return None; } - let rel = self.filter.rel(&f.path)?; - let prev_hash = self.data.read().get(&f.path).map(|e| e.hash); + let rel = filter.rel(&f.path)?; + let prev_hash = if force { None } else { self.data.read().get(&f.path).map(|e| e.hash) }; match self.extract(f, prev_hash) { Extracted::Unchanged => { // Same content, new mtime: keep the symbols, refresh metadata. @@ -288,7 +308,7 @@ impl Shared { return; } let puts: Vec<(String, FileRecord)> = - extracted.iter().map(|(rel, rec, _)| (rel.clone(), rec.clone())).collect(); + extracted.iter().filter_map(|(_, rec, path)| Some((filter.store_key(path)?, rec.clone()))).collect(); { let entries: Vec = extracted.into_iter().map(|(rel, rec, path)| FileEntry::new(path, rel, rec)).collect(); @@ -346,13 +366,24 @@ impl Shared { let mut changed: Vec = Vec::new(); let mut removed: Vec = Vec::new(); let mut save_recent = false; + let mut rescan = false; + let mut reparse: Vec> = Vec::new(); for job in jobs { match job { Job::Changed(p) => changed.extend(p), Job::Removed(p) => removed.extend(p), Job::SaveRecent => save_recent = true, + Job::Rescan => rescan = true, + Job::Reparse(before) => reparse.push(before), } } + if rescan { + // The scan also picks up whatever `changed` / `removed` reported. + self.full_scan(); + changed.clear(); + removed.clear(); + } + let filter = self.filter(); let mut touched = false; // Removals first; a path removed then re-created shows up in `changed` too. @@ -363,11 +394,11 @@ impl Shared { continue; } let mut d = self.data.write(); - if let Some(e) = d.remove(&path) { - deletes.push(e.rel.into_string()); - } else { - deletes.extend(d.remove_tree(&path)); - } + let gone = match d.remove(&path) { + Some(_) => vec![path], + None => d.remove_tree(&path), + }; + deletes.extend(gone.iter().filter_map(|p| filter.store_key(p))); } if !deletes.is_empty() { touched = true; @@ -381,11 +412,11 @@ impl Shared { continue; } let Ok(meta) = std::fs::symlink_metadata(&path) else { continue }; - if self.filter.is_excluded(&path, meta.is_dir()) { + if filter.is_excluded(&path, meta.is_dir()) { continue; } if meta.is_dir() { - files.extend(self.filter.walk(&path, &self.cancelled)); + files.extend(filter.walk(&path, &self.cancelled)); } else if meta.is_file() { files.push(Found { mtime: mtime_of(&meta), size: meta.len(), path }); } @@ -403,11 +434,32 @@ impl Shared { if self.is_cancelled() { return; } - self.index_files(chunk.iter()); + self.index_files(chunk.iter(), false); + } + } + for before in reparse { + let affected: Vec = { + let d = self.data.read(); + d.paths() + .filter(|p| before.detect(p, None) != self.languages.detect(p, None)) + .filter_map(|p| { + let e = d.get(p)?; + Some(Found { path: p.clone(), mtime: e.mtime, size: e.size }) + }) + .collect() + }; + if !affected.is_empty() { + touched = true; + for chunk in affected.chunks(CHUNK) { + if self.is_cancelled() { + return; + } + self.index_files(chunk.iter(), true); + } } } if save_recent && let Some(store) = &self.store { - let rels: Vec = self.recent.lock().iter().filter_map(|p| self.filter.rel(p)).collect(); + let rels: Vec = self.recent.lock().iter().filter_map(|p| filter.store_key(p)).collect(); if let Err(e) = store.save_recent(&rels) { tracing::warn!("index cache: saving recent files failed: {e}"); } diff --git a/crates/ide-language/src/lib.rs b/crates/ide-language/src/lib.rs index 8807d99a..ef476297 100644 --- a/crates/ide-language/src/lib.rs +++ b/crates/ide-language/src/lib.rs @@ -11,11 +11,11 @@ use std::collections::BTreeMap; mod registry; mod syntax; -pub use registry::LanguageRegistry; +pub use registry::{FileTypes, LanguageRegistry}; pub use syntax::{ CSS_CLASS, CSS_ID, CSS_VARIABLE, FileReference, Literal, LiteralKind, MAX_CACHED_TREES, MAX_PARSE_BYTES, - ParsedTree, Syntax, SyntaxCache, SyntaxCacheStats, SyntaxHighlightKind, SyntaxHighlights, TreeError, WebReference, - WebReferenceKind, + ParsedTree, SpellingKind, SpellingRegion, Syntax, SyntaxCache, SyntaxCacheStats, SyntaxHighlightKind, + SyntaxHighlights, TreeError, WebReference, WebReferenceKind, merge_visibility, }; /// Parsed form of `languages/.toml`. Backend-only; the UI gets [`LanguageInfo`]. @@ -119,6 +119,18 @@ pub struct LanguageInfo { #[serde(default)] #[specta(optional)] pub debuggable: bool, + /// Names of the configured language servers (Settings › Inspections). + #[serde(default)] + #[specta(optional)] + pub language_servers: Vec, + /// Exact file names and file-name patterns of the definition (Settings › File Types). + #[serde(default)] + #[specta(optional)] + pub filenames: Vec, + /// First-line regex of the definition (shebangs, `, } impl From<&LanguageConfig> for LanguageInfo { @@ -132,6 +144,9 @@ impl From<&LanguageConfig> for LanguageInfo { block_comment: c.block_comment.clone(), indent: c.indent, debuggable: c.debugger.is_some(), + language_servers: c.language_servers.iter().map(|s| s.name.clone()).collect(), + filenames: c.filenames.clone(), + first_line: c.first_line.clone(), } } } diff --git a/crates/ide-language/src/registry.rs b/crates/ide-language/src/registry.rs index 84fd9ac2..29cd289c 100644 --- a/crates/ide-language/src/registry.rs +++ b/crates/ide-language/src/registry.rs @@ -1,7 +1,10 @@ use crate::LanguageConfig; +use ide_core::settings::{FileSettings, FileTypeAssociation}; use ide_core::{IdeError, IdeResult, LanguageId}; -use std::collections::HashMap; -use std::path::Path; +use parking_lot::RwLock; +use std::collections::{BTreeMap, HashMap}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; /// Built-in definitions, embedded at compile time. Add new languages here. const BUILTIN: &[(&str, &str)] = &[ @@ -78,11 +81,130 @@ const BUILTIN: &[(&str, &str)] = &[ pub struct LanguageRegistry { languages: Vec, + /// Lookup tables of the definitions with the user's file types applied. + tables: RwLock>, + file_types: RwLock, +} + +/// The user's file type associations (Settings › Editor › File Types) and +/// per-file overrides (Override File Type), resolved for one project. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct FileTypes { + /// Language id → patterns added to / removed from its definition. + pub associations: BTreeMap, + /// Absolute path → language id. + pub overrides: HashMap, +} + +impl FileTypes { + /// From `files.fileTypes` / `files.fileTypeOverrides`; relative override paths resolve against `root`. + pub fn from_settings(files: &FileSettings, root: Option<&Path>) -> Self { + let overrides = files + .file_type_overrides + .iter() + .filter(|(_, lang)| !lang.trim().is_empty()) + .filter_map(|(path, lang)| { + let p = Path::new(path.as_str()); + let abs = if p.is_absolute() { p.to_path_buf() } else { root?.join(p) }; + Some((abs, LanguageId::new(lang.trim()))) + }) + .collect(); + Self { associations: files.file_types.clone(), overrides } + } +} + +#[derive(Default)] +struct Tables { by_ext: HashMap, by_name: HashMap, - /// File-name patterns containing `*`, e.g. `Dockerfile.*`, `tsconfig.*.json`. + /// File-name patterns with wildcards, e.g. `Dockerfile.*`, `tsconfig.*.json`; the user's first. name_globs: Vec<(String, usize)>, + /// Interpreter names of user hashbang patterns. + hashbangs: Vec<(String, usize)>, first_line: Vec<(regex_lite::Regex, usize)>, + overrides: HashMap, +} + +/// How a file name pattern is looked up. +enum Pattern<'a> { + /// `*.ext` (lower-cased). + Ext(String), + Name(&'a str), + Glob(&'a str), +} + +fn classify(pattern: &str) -> Pattern<'_> { + let wild = |s: &str| s.contains(['*', '?']); + match pattern.strip_prefix("*.") { + Some(ext) if !ext.is_empty() && !wild(ext) => Pattern::Ext(ext.to_ascii_lowercase()), + _ if wild(pattern) => Pattern::Glob(pattern), + _ => Pattern::Name(pattern), + } +} + +impl Tables { + fn build(languages: &[LanguageConfig], file_types: &FileTypes) -> Self { + let mut t = Tables::default(); + let index_of = |id: &str| languages.iter().position(|l| l.id.as_str() == id); + let removed = |l: &LanguageConfig, pattern: &str| { + file_types.associations.get(l.id.as_str()).is_some_and(|a| { + a.removed_patterns.iter().any(|r| r.trim() == pattern || r.trim().eq_ignore_ascii_case(pattern)) + }) + }; + let mut builtin_globs = Vec::new(); + for (i, l) in languages.iter().enumerate() { + for ext in &l.extensions { + if !removed(l, &format!("*.{ext}")) { + t.by_ext.insert(ext.to_ascii_lowercase(), i); + } + } + for name in l.filenames.iter().filter(|n| !removed(l, n)) { + if name.contains(['*', '?']) { + builtin_globs.push((name.clone(), i)); + } else { + t.by_name.insert(name.clone(), i); + } + } + if let Some(pattern) = l.first_line.as_deref() { + match regex_lite::Regex::new(pattern) { + Ok(re) => t.first_line.push((re, i)), + Err(e) => tracing::warn!("language {}: invalid first-line pattern: {e}", l.id), + } + } + } + // The user's patterns win over the definitions'. + let mut user_globs = Vec::new(); + for (id, assoc) in &file_types.associations { + let Some(i) = index_of(id) else { continue }; + for pattern in assoc.patterns.iter().map(|p| p.trim()).filter(|p| !p.is_empty()) { + match classify(pattern) { + Pattern::Ext(ext) => { + t.by_ext.insert(ext, i); + } + Pattern::Name(name) => { + t.by_name.insert(name.to_owned(), i); + } + Pattern::Glob(glob) => user_globs.push((glob.to_owned(), i)), + } + } + for h in assoc.hashbangs.iter().map(|h| h.trim()).filter(|h| !h.is_empty()) { + t.hashbangs.push((h.to_owned(), i)); + } + } + // More specific (longer) patterns first: `tsconfig.*.json` before `*.json`-like ones. + user_globs.sort_by_key(|(p, _): &(String, usize)| std::cmp::Reverse(p.len())); + builtin_globs.sort_by_key(|(p, _): &(String, usize)| std::cmp::Reverse(p.len())); + t.name_globs = user_globs.into_iter().chain(builtin_globs).collect(); + t.overrides = + file_types.overrides.iter().filter_map(|(p, id)| Some((p.clone(), index_of(id.as_str())?))).collect(); + t + } +} + +/// Whether the `#!` line runs `interpreter` (`#!/usr/bin/env node`, `#!/bin/node -x` for `node`). +fn hashbang_runs(line: &str, interpreter: &str) -> bool { + let Some(rest) = line.strip_prefix("#!") else { return false }; + rest.split(|c: char| c.is_whitespace() || c == '/').any(|token| token == interpreter) } impl LanguageRegistry { @@ -116,31 +238,31 @@ impl LanguageRegistry { None => languages.push(cfg), } } - let mut by_ext = HashMap::new(); - let mut by_name = HashMap::new(); - let mut name_globs = Vec::new(); - let mut first_line = Vec::new(); - for (i, l) in languages.iter().enumerate() { - for ext in &l.extensions { - by_ext.insert(ext.to_ascii_lowercase(), i); - } - for name in &l.filenames { - if name.contains('*') { - name_globs.push((name.clone(), i)); - } else { - by_name.insert(name.clone(), i); - } - } - if let Some(pattern) = l.first_line.as_deref() { - match regex_lite::Regex::new(pattern) { - Ok(re) => first_line.push((re, i)), - Err(e) => tracing::warn!("language {}: invalid first-line pattern: {e}", l.id), - } - } + let tables = Tables::build(&languages, &FileTypes::default()); + Self { languages, tables: RwLock::new(Arc::new(tables)), file_types: RwLock::default() } + } + + /// A registry with the same definitions and no user file types (one per project). + pub fn fork(&self) -> Self { + Self::from_configs(self.languages.clone()) + } + + /// A copy with the same definitions and file types (to compare detection before / after a change). + pub fn snapshot(&self) -> Self { + let copy = self.fork(); + copy.set_file_types(self.file_types.read().clone()); + copy + } + + /// Applies the user's file types; returns whether they changed. + pub fn set_file_types(&self, file_types: FileTypes) -> bool { + let mut current = self.file_types.write(); + if *current == file_types { + return false; } - // More specific (longer) patterns first: `tsconfig.*.json` before `*.json`-like ones. - name_globs.sort_by_key(|(p, _): &(String, usize)| std::cmp::Reverse(p.len())); - Self { languages, by_ext, by_name, name_globs, first_line } + *self.tables.write() = Arc::new(Tables::build(&self.languages, &file_types)); + *current = file_types; + true } pub fn all(&self) -> Vec { @@ -151,48 +273,67 @@ impl LanguageRegistry { self.languages.iter().find(|l| &l.id == id).cloned() } - /// Detects by exact file name, then file-name pattern (`Dockerfile.*`), then - /// extension (longest compound first, e.g. `d.ts`), then first line; falls - /// back to plaintext. + /// Detects by Override File Type, exact file name, then file-name pattern + /// (`Dockerfile.*`), then extension (longest compound first, e.g. `d.ts`), + /// then hashbang / first line; falls back to plaintext. The user's patterns + /// win over the definitions'. pub fn detect(&self, path: &Path, first_line: Option<&str>) -> LanguageId { - let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or_default(); - if let Some(&i) = self.by_name.get(file_name) { + let t = self.tables.read().clone(); + if !t.overrides.is_empty() + && let Some(&i) = t.overrides.get(path) + { return self.languages[i].id.clone(); } - if let Some((_, i)) = self.name_globs.iter().find(|(p, _)| wildcard_match(p, file_name)) { + let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or_default(); + if let Some(&i) = t.by_name.get(file_name) { + return self.languages[i].id.clone(); + } + if let Some((_, i)) = t.name_globs.iter().find(|(p, _)| wildcard_match(p, file_name)) { return self.languages[*i].id.clone(); } let lower = file_name.to_ascii_lowercase(); let mut rest = lower.as_str(); while let Some(dot) = rest.find('.') { rest = &rest[dot + 1..]; - if let Some(&i) = self.by_ext.get(rest) { + if let Some(&i) = t.by_ext.get(rest) { return self.languages[i].id.clone(); } } - if let Some(line) = first_line - && let Some((_, i)) = self.first_line.iter().find(|(re, _)| re.is_match(line)) - { - return self.languages[*i].id.clone(); + if let Some(line) = first_line { + if let Some((_, i)) = t.hashbangs.iter().find(|(h, _)| hashbang_runs(line, h)) { + return self.languages[*i].id.clone(); + } + if let Some((_, i)) = t.first_line.iter().find(|(re, _)| re.is_match(line)) { + return self.languages[*i].id.clone(); + } } LanguageId::plain_text() } } -/// `*` matches any (possibly empty) run of characters; everything else is literal. +/// `*` matches any (possibly empty) run of characters, `?` exactly one; everything else is literal. fn wildcard_match(pattern: &str, name: &str) -> bool { - let mut parts = pattern.split('*'); - let first = parts.next().unwrap_or_default(); - let Some(mut rest) = name.strip_prefix(first) else { return false }; - let parts: Vec<&str> = parts.collect(); - let Some((last, middle)) = parts.split_last() else { return rest.is_empty() }; - for part in middle { - match rest.find(part) { - Some(i) => rest = &rest[i + part.len()..], - None => return false, + let p: Vec = pattern.chars().collect(); + let n: Vec = name.chars().collect(); + let (mut pi, mut ni) = (0, 0); + // Position after the last `*` and the name position it is matched up to. + let mut star: Option<(usize, usize)> = None; + while ni < n.len() { + if pi < p.len() && (p[pi] == '?' || p[pi] == n[ni]) { + pi += 1; + ni += 1; + } else if pi < p.len() && p[pi] == '*' { + star = Some((pi + 1, ni)); + pi += 1; + } else if let Some((sp, sn)) = star { + pi = sp; + ni = sn + 1; + star = Some((sp, sn + 1)); + } else { + return false; } } - rest.len() >= last.len() && rest.ends_with(last) + p[pi..].iter().all(|&c| c == '*') } fn parse(src: &str) -> Result { @@ -573,6 +714,60 @@ mod tests { assert!(wildcard_match("*.Dockerfile", "web.Dockerfile")); assert!(wildcard_match("a*b*c", "aXbYc")); assert!(!wildcard_match("a*b*c", "aXcYb")); + assert!(wildcard_match("file?.txt", "file1.txt")); + assert!(!wildcard_match("file?.txt", "file.txt")); + assert!(wildcard_match("*", "")); + assert!(wildcard_match("a**", "abc")); + } + + fn assoc(patterns: &[&str], removed: &[&str], hashbangs: &[&str]) -> FileTypeAssociation { + let v = |xs: &[&str]| xs.iter().map(|s| (*s).to_owned()).collect(); + FileTypeAssociation { patterns: v(patterns), removed_patterns: v(removed), hashbangs: v(hashbangs) } + } + + #[test] + fn user_file_types_add_remove_and_win() { + let reg = reg(); + let mut ft = FileTypes::default(); + ft.associations + .insert("python".into(), assoc(&["*.foo", "Pipfile.lock", "build_*.cfg", "*.LOG"], &[], &["mypy"])); + ft.associations.insert("groovy".into(), assoc(&[], &["Jenkinsfile"], &[])); + ft.associations.insert("shellscript".into(), assoc(&["Jenkinsfile"], &[], &[])); + ft.associations.insert("rust".into(), assoc(&[], &["*.rs"], &[])); + assert!(reg.set_file_types(ft.clone())); + assert!(!reg.set_file_types(ft), "unchanged file types report no change"); + assert_eq!(detect(®, "a/x.foo"), "python"); + assert_eq!(detect(®, "x.FOO"), "python"); + assert_eq!(detect(®, "Pipfile.lock"), "python"); + assert_eq!(detect(®, "build_dev.cfg"), "python"); + assert_eq!(detect(®, "setup.cfg"), "ini"); + assert_eq!(detect(®, "server.log"), "python", "a user extension overrides a built-in one"); + assert_eq!(detect(®, "Jenkinsfile"), "shellscript"); + assert_eq!(detect(®, "main.rs"), "plaintext"); + assert_eq!(reg.detect(Path::new("tool"), Some("#!/usr/bin/env mypy --strict")).as_str(), "python"); + assert_eq!(reg.detect(Path::new("tool"), Some("#!/usr/bin/env mypyc")).as_str(), "plaintext"); + reg.set_file_types(FileTypes::default()); + assert_eq!(detect(®, "main.rs"), "rust"); + assert_eq!(detect(®, "x.foo"), "plaintext"); + } + + #[test] + fn override_file_type_per_file() { + let reg = reg().fork(); + let files = FileSettings { + file_type_overrides: [ + ("notes.txt".to_owned(), "markdown".to_owned()), + ("/abs/run".to_owned(), "python".to_owned()), + ("bad.txt".to_owned(), "no-such-language".to_owned()), + ] + .into(), + ..Default::default() + }; + reg.set_file_types(FileTypes::from_settings(&files, Some(Path::new("/proj")))); + assert_eq!(detect(®, "/proj/notes.txt"), "markdown"); + assert_eq!(detect(®, "/other/notes.txt"), "plaintext"); + assert_eq!(detect(®, "/abs/run"), "python"); + assert_eq!(detect(®, "/proj/bad.txt"), "plaintext"); } #[test] diff --git a/crates/ide-language/src/syntax/literals.rs b/crates/ide-language/src/syntax/literals.rs index 78031fdd..4a784d76 100644 --- a/crates/ide-language/src/syntax/literals.rs +++ b/crates/ide-language/src/syntax/literals.rs @@ -2,7 +2,7 @@ //! filter ("In comments", "Except string literals", …). use super::folding::is_comment; -use tree_sitter::{Node, Tree}; +use tree_sitter::{Node, Tree, TreeCursor}; /// What a [`Literal`] range is. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -57,3 +57,70 @@ pub(crate) fn literals(tree: &Tree) -> Vec { } } } + +/// What a [`SpellingRegion`] covers. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SpellingKind { + Comment, + String, + /// The name a declaration introduces (not its uses). + Name, +} + +/// Text the Typo inspection checks: byte range in document order. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SpellingRegion { + pub start: usize, + pub end: usize, + pub kind: SpellingKind, +} + +fn is_name_leaf(kind: &str) -> bool { + kind.contains("identifier") || kind == "name" +} + +/// Whether the identifier leaf under `cursor` is the name a declaration +/// introduces (`fn name`, `let pattern`, `int *declarator`, parameters, +/// `name = …` assignments) rather than a use of one. +fn declares(cursor: &TreeCursor<'_>, node: &Node<'_>) -> bool { + let parent = node.parent().map(|p| p.kind()).unwrap_or_default(); + match cursor.field_name() { + Some("name" | "pattern" | "declarator") => true, + Some("left") => parent.contains("assignment") || parent.contains("declarator"), + _ => parent.contains("parameter"), + } +} + +/// Comments, string literals and declared names, for spell checking. +pub(crate) fn spelling_regions(tree: &Tree) -> Vec { + let mut out = Vec::new(); + let mut cursor = tree.walk(); + loop { + let node = cursor.node(); + let literal = kind_of(&node); + let region = |kind| SpellingRegion { start: node.start_byte(), end: node.end_byte(), kind }; + match literal { + Some(LiteralKind::Comment) => out.push(region(SpellingKind::Comment)), + Some(LiteralKind::String) => out.push(region(SpellingKind::String)), + None if node.is_named() + && node.child_count() == 0 + && is_name_leaf(node.kind()) + && declares(&cursor, &node) => + { + out.push(region(SpellingKind::Name)) + } + None => {} + } + if literal.is_none() && cursor.goto_first_child() { + continue; + } + loop { + if cursor.goto_next_sibling() { + break; + } + if !cursor.goto_parent() { + return out; + } + } + } +} diff --git a/crates/ide-language/src/syntax/mod.rs b/crates/ide-language/src/syntax/mod.rs index 879cbb64..776acdd4 100644 --- a/crates/ide-language/src/syntax/mod.rs +++ b/crates/ide-language/src/syntax/mod.rs @@ -15,7 +15,8 @@ pub use cache::{MAX_CACHED_TREES, SyntaxCache, SyntaxCacheStats}; use grammars::{SPECS, Spec}; pub use highlight::{SyntaxHighlightKind, SyntaxHighlights}; use ide_core::{LanguageId, OutlineItem, Pos, Range}; -pub use literals::{Literal, LiteralKind}; +pub use literals::{Literal, LiteralKind, SpellingKind, SpellingRegion}; +pub use outline::merge_visibility; use std::cell::RefCell; use std::collections::HashMap; use std::sync::OnceLock; @@ -235,6 +236,12 @@ impl Syntax { self.analyze(lang, text, |_, tree, _| Some(literals::literals(tree))) } + /// Comments, string literals and declared names (byte ranges, document + /// order) for the Typo inspection; `None` like [`Self::literals`]. + pub fn spelling_regions(&self, lang: &LanguageId, text: &str) -> Option> { + self.analyze(lang, text, |_, tree, _| Some(literals::spelling_regions(tree))) + } + /// Syntax-aware selection ranges for IDEA "Extend Selection" (⌥↑), innermost first. pub fn selection_ranges(&self, lang: &LanguageId, text: &str, offset_utf16: u32) -> Vec { match self.analyze(lang, text, |_, tree, text| Some(selection::selection_ranges(tree, text, offset_utf16))) { diff --git a/crates/ide-language/src/syntax/outline.rs b/crates/ide-language/src/syntax/outline.rs index 541513d5..acae3590 100644 --- a/crates/ide-language/src/syntax/outline.rs +++ b/crates/ide-language/src/syntax/outline.rs @@ -15,7 +15,7 @@ use super::Grammar; use super::lines::LineIndex; use crate::SymbolTag; -use ide_core::{OutlineItem, SymbolKind}; +use ide_core::{OutlineItem, SymbolKind, Visibility}; use std::collections::HashMap; use tree_sitter::{Node, Query, QueryCursor, StreamingIterator, Tree}; @@ -139,11 +139,16 @@ struct Raw { container_name: String, explicit_container: Option, detail: Option, + /// From a modifier keyword in the declaration (`pub`, `private`, ...). + visibility: Option, + /// Rust `impl Trait for Type`: members take the trait's visibility. + trait_impl: bool, } struct Built { raw: Raw, kind: SymbolKind, + visibility: Option, children: Vec, } @@ -188,6 +193,7 @@ fn collect(g: &Grammar, q: &OutlineQuery, tree: &Tree, text: &str) -> Vec base = last.to_owned(); kind = ItemKind::Symbol(SymbolKind::Method); } + let trait_impl = kind == ItemKind::Impl && trait_.is_some(); let name = match (kind, trait_) { (ItemKind::Impl, Some(t)) => format!("impl {} for {base}", clean(node_text(&t, text), 120)), (ItemKind::Impl, None) => format!("impl {base}"), @@ -213,6 +219,8 @@ fn collect(g: &Grammar, q: &OutlineQuery, tree: &Tree, text: &str) -> Vec container_name, explicit_container: container, detail: detail.map(|d| clean(node_text(&d, text), 80)).filter(|d| !d.is_empty()), + visibility: explicit_visibility(&node, text), + trait_impl, }; match by_node.get(&raw.node_id) { Some(existing) if existing.pattern <= raw.pattern => {} @@ -239,6 +247,7 @@ fn build_tree(g: &Grammar, raws: Vec) -> Vec { push_child(&mut stack, &mut roots, done); } let parent_kind = stack.last().map(|p| p.raw.kind); + let parent_trait_impl = stack.last().is_some_and(|p| p.raw.trait_impl); let mut kind = raw.kind.symbol_kind(); if kind == SymbolKind::Function && parent_kind.is_some_and(ItemKind::holds_methods) { kind = SymbolKind::Method; @@ -250,7 +259,10 @@ fn build_tree(g: &Grammar, raws: Vec) -> Vec { kind = SymbolKind::Constructor; } } - stack.push(Built { raw, kind, children: Vec::new() }); + let visibility = raw + .visibility + .or_else(|| implicit_visibility(g.spec.name, raw.kind, kind, &raw.name, parent_kind, parent_trait_impl)); + stack.push(Built { raw, kind, visibility, children: Vec::new() }); } while let Some(done) = stack.pop() { push_child(&mut stack, &mut roots, done); @@ -278,6 +290,7 @@ pub(crate) fn outline(g: &Grammar, q: &OutlineQuery, tree: &Tree, text: &str) -> range: b.raw.range, selection_range: b.raw.sel, children: b.children.into_iter().map(convert).collect(), + visibility: b.visibility, } } collect(g, q, tree, text).into_iter().map(convert).collect() @@ -308,6 +321,107 @@ pub(crate) fn tags(g: &Grammar, q: &OutlineQuery, tree: &Tree, text: &str) -> Ve out } +/// Visibility spelled out by a modifier child (`visibility_modifier`, `modifiers`, +/// `accessibility_modifier`, ...) of the declaration. +fn explicit_visibility(node: &Node, text: &str) -> Option { + let mut cursor = node.walk(); + let mut found: Option = None; + for child in node.children(&mut cursor) { + if !child.kind().contains("modifier") { + continue; + } + let t = node_text(&child, text); + if child.kind() == "visibility_modifier" && t.starts_with("pub") { + // Rust: `pub` / `pub(crate)` / `pub(super)` / `pub(in path)`. + let v = if t.trim() == "pub" { Visibility::Public } else { Visibility::Package }; + found = Some(found.map_or(v, |f| narrower(f, v))); + continue; + } + for word in t.split(|c: char| !c.is_ascii_alphanumeric() && c != '_') { + let v = match word { + "public" | "open" => Visibility::Public, + "protected" => Visibility::Protected, + "internal" | "fileprivate" => Visibility::Package, + "private" => Visibility::Private, + _ => continue, + }; + found = Some(found.map_or(v, |f| narrower(f, v))); + } + } + found +} + +/// Several keywords (C# `private protected`): the narrower one wins. +fn narrower(a: Visibility, b: Visibility) -> Visibility { + let rank = |v: Visibility| match v { + Visibility::Public => 0, + Visibility::Protected => 1, + Visibility::Package => 2, + Visibility::Private => 3, + }; + if rank(a) >= rank(b) { a } else { b } +} + +/// The language's default when no modifier is written. +fn implicit_visibility( + lang: &str, + item: ItemKind, + kind: SymbolKind, + name: &str, + parent: Option, + parent_trait_impl: bool, +) -> Option { + use SymbolKind as K; + if matches!(item, ItemKind::Impl | ItemKind::Extension | ItemKind::Heading) + || matches!(kind, K::EnumMember | K::Package | K::Namespace) + { + return None; + } + let in_interface = matches!(parent, Some(ItemKind::Symbol(K::Interface | K::Trait))); + match lang { + "rust" => (!in_interface && !parent_trait_impl && kind != K::Macro).then_some(Visibility::Private), + "java" => (!in_interface).then_some(Visibility::Package), + "csharp" => match parent { + _ if in_interface => None, + Some(ItemKind::Symbol(K::Class | K::Struct | K::Enum)) => Some(Visibility::Private), + _ => Some(Visibility::Package), + }, + "go" => name.starts_with(|c: char| c.is_lowercase() || c == '_').then_some(Visibility::Package), + "python" => { + (name.starts_with('_') && !(name.starts_with("__") && name.ends_with("__"))).then_some(Visibility::Private) + } + "dart" => name.starts_with('_').then_some(Visibility::Private), + "typescript" | "tsx" | "javascript" => name.starts_with('#').then_some(Visibility::Private), + _ => None, + } +} + +/// Copies tree-sitter visibility onto language-server symbols (which carry none), +/// matching by name and identifier start. +pub fn merge_visibility(target: &mut [OutlineItem], source: &[OutlineItem]) { + fn index(items: &[OutlineItem], out: &mut HashMap<(ide_core::Pos, String), Visibility>) { + for i in items { + if let Some(v) = i.visibility { + out.insert((i.selection_range.start, i.name.clone()), v); + } + index(&i.children, out); + } + } + fn apply(items: &mut [OutlineItem], map: &HashMap<(ide_core::Pos, String), Visibility>) { + for i in items { + if i.visibility.is_none() { + i.visibility = map.get(&(i.selection_range.start, i.name.clone())).copied(); + } + apply(&mut i.children, map); + } + } + let mut map = HashMap::new(); + index(source, &mut map); + if !map.is_empty() { + apply(target, &map); + } +} + /// End of `node` without trailing whitespace (C macros and others include the newline). fn trimmed_end(node: &Node, text: &str) -> usize { let (s, e) = (node.start_byte(), node.end_byte()); diff --git a/crates/ide-language/src/syntax/tests.rs b/crates/ide-language/src/syntax/tests.rs index 293fe341..80614378 100644 --- a/crates/ide-language/src/syntax/tests.rs +++ b/crates/ide-language/src/syntax/tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::FoldKind; -use ide_core::{OutlineItem, Pos, SymbolKind}; +use ide_core::{OutlineItem, Pos, SymbolKind, Visibility}; fn syntax() -> &'static Syntax { static S: OnceLock = OnceLock::new(); @@ -1187,3 +1187,109 @@ fn literals_are_outermost_comments_and_strings() { ); assert!(syntax().literals(&lang("plaintext"), "// x").is_none()); } + +/// `name: visibility` for every outline item, depth-first. +fn visibilities(id: &str, src: &str) -> Vec<(String, Option)> { + fn go(items: &[OutlineItem], out: &mut Vec<(String, Option)>) { + for i in items { + out.push((i.name.clone(), i.visibility)); + go(&i.children, out); + } + } + let mut out = Vec::new(); + go(&syntax().outline(&lang(id), src), &mut out); + out +} + +fn vis_of(list: &[(String, Option)], name: &str) -> Option { + list.iter().find(|(n, _)| n == name).unwrap_or_else(|| panic!("no {name} in {list:?}")).1 +} + +#[test] +fn outline_visibility_rust() { + let src = "pub struct S { a: i32, pub b: i32, pub(crate) c: i32 }\n\ + trait T { fn req(&self); }\n\ + impl T for S { fn req(&self) {} }\n\ + impl S { pub fn new() {} fn helper() {} }\n\ + fn main() {}\n"; + let v = visibilities("rust", src); + assert_eq!(vis_of(&v, "S"), Some(Visibility::Public)); + assert_eq!(vis_of(&v, "a"), Some(Visibility::Private)); + assert_eq!(vis_of(&v, "b"), Some(Visibility::Public)); + assert_eq!(vis_of(&v, "c"), Some(Visibility::Package)); + assert_eq!(vis_of(&v, "new"), Some(Visibility::Public)); + assert_eq!(vis_of(&v, "helper"), Some(Visibility::Private)); + assert_eq!(vis_of(&v, "main"), Some(Visibility::Private)); + assert_eq!(vis_of(&v, "impl T for S"), None); + // Trait members and trait impl members follow the trait. + assert!(v.iter().filter(|(n, _)| n == "req").all(|(_, vis)| vis.is_none())); +} + +#[test] +fn outline_visibility_java_ts_python() { + let java = "public class A {\n private int x;\n protected void p() {}\n void pkg() {}\n public A() {}\n}\ninterface I { void m(); }\n"; + let v = visibilities("java", java); + assert_eq!(vis_of(&v, "x"), Some(Visibility::Private)); + assert_eq!(vis_of(&v, "p"), Some(Visibility::Protected)); + assert_eq!(vis_of(&v, "pkg"), Some(Visibility::Package)); + assert_eq!(vis_of(&v, "A"), Some(Visibility::Public)); + assert_eq!(vis_of(&v, "m"), None); + + let ts = "class C {\n private a = 1;\n protected b(): void {}\n c(): void {}\n #d = 2;\n}\n"; + let v = visibilities("typescript", ts); + assert_eq!(vis_of(&v, "a"), Some(Visibility::Private)); + assert_eq!(vis_of(&v, "b"), Some(Visibility::Protected)); + assert_eq!(vis_of(&v, "c"), None); + assert_eq!(vis_of(&v, "#d"), Some(Visibility::Private)); + + let py = "class K:\n def _hidden(self): pass\n def __init__(self): pass\n def shown(self): pass\n"; + let v = visibilities("python", py); + assert_eq!(vis_of(&v, "_hidden"), Some(Visibility::Private)); + assert_eq!(vis_of(&v, "__init__"), None); + assert_eq!(vis_of(&v, "shown"), None); +} + +#[test] +fn merge_visibility_matches_by_name_and_position() { + fn clear(items: &mut [OutlineItem]) { + for i in items { + i.visibility = None; + clear(&mut i.children); + } + } + let src = "struct S { a: i32 }\nfn f() {}\n"; + let ts = syntax().outline(&lang("rust"), src); + let mut lsp = ts.clone(); + clear(&mut lsp); + lsp[1].name = "renamed".into(); + crate::merge_visibility(&mut lsp, &ts); + assert_eq!(lsp[0].visibility, Some(Visibility::Private)); + assert_eq!(lsp[0].children[0].visibility, Some(Visibility::Private)); + assert_eq!(lsp[1].visibility, None); +} + +#[test] +fn spelling_regions_cover_declared_names_only() { + let src = "// note\nfn parse_input(raw_text: &str) -> Value { let mut counter = helper(raw_text); \"hi\" }\n"; + let found: Vec<(&str, SpellingKind)> = + syntax().spelling_regions(&lang("rust"), src).unwrap().iter().map(|r| (&src[r.start..r.end], r.kind)).collect(); + assert_eq!( + found, + vec![ + ("// note", SpellingKind::Comment), + ("parse_input", SpellingKind::Name), + ("raw_text", SpellingKind::Name), + ("counter", SpellingKind::Name), + ("\"hi\"", SpellingKind::String), + ] + ); + let py = "def load_config(path, retries=3):\n \"\"\"Doc.\"\"\"\n total = 0\n"; + let names: Vec<&str> = syntax() + .spelling_regions(&lang("python"), py) + .unwrap() + .iter() + .filter(|r| r.kind == SpellingKind::Name) + .map(|r| &py[r.start..r.end]) + .collect(); + assert_eq!(names, ["load_config", "path", "retries", "total"]); +} diff --git a/crates/ide-lsp/src/caps.rs b/crates/ide-lsp/src/caps.rs index 39b51ff0..da53501b 100644 --- a/crates/ide-lsp/src/caps.rs +++ b/crates/ide-lsp/src/caps.rs @@ -157,6 +157,7 @@ pub fn client_capabilities() -> Value { "augmentsSyntaxTokens": true }, "inlayHint": { "dynamicRegistration": false }, + "inlineCompletion": { "dynamicRegistration": true }, "callHierarchy": { "dynamicRegistration": false }, "typeHierarchy": { "dynamicRegistration": false } }, @@ -204,6 +205,7 @@ pub fn capability_key(method: &str) -> Option<&'static str> { "textDocument/formatting" => "documentFormattingProvider", "textDocument/rangeFormatting" => "documentRangeFormattingProvider", "textDocument/inlayHint" => "inlayHintProvider", + "textDocument/inlineCompletion" => "inlineCompletionProvider", "textDocument/semanticTokens" | "textDocument/semanticTokens/full" => "semanticTokensProvider", "textDocument/codeLens" => "codeLensProvider", "textDocument/documentLink" => "documentLinkProvider", diff --git a/crates/ide-lsp/src/convert.rs b/crates/ide-lsp/src/convert.rs index 34646c6a..eadfb18d 100644 --- a/crates/ide-lsp/src/convert.rs +++ b/crates/ide-lsp/src/convert.rs @@ -434,6 +434,101 @@ pub fn lsp_completion_item(text: &DocText, enc: Encoding, item: &CompletionItem) } } +// ---------------------------------------------------------------- inline completion + +/// `InlineCompletionList | InlineCompletionItem[] | null` → items (malformed ones skipped). +pub fn inline_completion_items(conv: &Conv, path: &Path, v: Value, server: ServerId) -> Vec { + let items = match v { + Value::Array(a) => a, + Value::Object(mut o) => match o.remove("items") { + Some(Value::Array(a)) => a, + _ => Vec::new(), + }, + _ => Vec::new(), + }; + items + .into_iter() + .filter_map(|it| { + let insert_text = match it.get("insertText")? { + Value::String(s) => s.clone(), + Value::Object(o) if o.get("kind").and_then(Value::as_str) == Some("snippet") => { + snippet_text(o.get("value")?.as_str()?) + } + _ => return None, + }; + let range = it + .get("range") + .and_then(|r| serde_json::from_value::(r.clone()).ok()) + .map(|r| conv.range(path, r)); + let filter_text = it.get("filterText").and_then(Value::as_str).map(str::to_owned); + let cmd = it.get("command").and_then(|c| serde_json::from_value::(c.clone()).ok()); + Some(InlineCompletionItem { insert_text, range, filter_text, command: cmd.map(command), server }) + }) + .filter(|i| !i.insert_text.is_empty()) + .collect() +} + +/// The text a snippet inserts with every placeholder at its default: +/// `foo(${1:a}, $2)$0` → `foo(a, )`; a choice takes its first option. +pub fn snippet_text(snippet: &str) -> String { + fn parse(chars: &[char], i: &mut usize, out: &mut String, nested: bool) { + while let Some(&ch) = chars.get(*i) { + let next = chars.get(*i + 1).copied(); + match ch { + '\\' if next.is_some() => { + out.extend(next); + *i += 2; + } + '}' if nested => { + *i += 1; + return; + } + '$' if next.is_some_and(|c| c.is_ascii_digit()) => { + *i += 1; + while chars.get(*i).is_some_and(char::is_ascii_digit) { + *i += 1; + } + } + '$' if next == Some('{') => { + *i += 2; + while chars.get(*i).is_some_and(|c| c.is_ascii_alphanumeric() || *c == '_') { + *i += 1; + } + match chars.get(*i) { + Some(':') => { + *i += 1; + parse(chars, i, out, true); + } + Some('|') => { + *i += 1; + let mut first = true; + while let Some(&c) = chars.get(*i).filter(|c| **c != '|') { + first &= c != ','; + if first { + out.push(c); + } + *i += 1; + } + *i = (*i + 2).min(chars.len()); + } + // `${1}`, or a variable without a default. + Some('}') => *i += 1, + _ => {} + } + } + _ => { + out.push(ch); + *i += 1; + } + } + } + } + let chars: Vec = snippet.chars().collect(); + let mut out = String::new(); + parse(&chars, &mut 0, &mut out, false); + out +} + // ---------------------------------------------------------------- commands & actions pub fn command(c: lsp::Command) -> Command { @@ -614,6 +709,7 @@ pub fn document_symbols(conv: &Conv, path: &Path, r: lsp::DocumentSymbolResponse range, selection_range: range, children: Vec::new(), + visibility: None, } }) .collect(); @@ -632,6 +728,7 @@ fn document_symbol(conv: &Conv, path: &Path, s: lsp::DocumentSymbol) -> OutlineI range: conv.range(path, s.range), selection_range: conv.range(path, s.selection_range), children: s.children.unwrap_or_default().into_iter().map(|c| document_symbol(conv, path, c)).collect(), + visibility: None, } } @@ -879,6 +976,7 @@ pub fn diagnostic(conv: &Conv, path: &Path, d: lsp::Diagnostic, server_name: &st }), unnecessary: tags.contains(&lsp::DiagnosticTag::UNNECESSARY), deprecated: tags.contains(&lsp::DiagnosticTag::DEPRECATED), + server: Some(server_name.to_owned()), } } @@ -892,6 +990,25 @@ mod tests { use super::*; use serde_json::json; + #[test] + fn snippet_text_takes_placeholder_defaults() { + assert_eq!(snippet_text("foo(${1:a}, $2)$0"), "foo(a, )"); + assert_eq!(snippet_text("${1:outer ${2:inner}} \\$x ${3|one,two|}${TM_FILENAME}"), "outer inner $x one"); + assert_eq!(snippet_text("plain"), "plain"); + } + + #[test] + fn inline_completion_shapes() { + let conv = Conv::utf16(); + let path = Path::new("/a.rs"); + let list = inline_completion_items(&conv, path, json!([{ "insertText": "x" }]), ServerId(1)); + assert_eq!(list.len(), 1); + assert!(inline_completion_items(&conv, path, Value::Null, ServerId(1)).is_empty()); + assert!( + inline_completion_items(&conv, path, json!({ "items": [{ "label": "no text" }] }), ServerId(1)).is_empty() + ); + } + fn p(l: u32, c: u32) -> Pos { Pos::new(l, c) } diff --git a/crates/ide-lsp/src/lib.rs b/crates/ide-lsp/src/lib.rs index 5e7994ea..ee171c36 100644 --- a/crates/ide-lsp/src/lib.rs +++ b/crates/ide-lsp/src/lib.rs @@ -804,6 +804,40 @@ impl LspManager { out.finish(CompletionList { is_incomplete: false, items: Vec::new() }) } + /// LSP 3.18 `textDocument/inlineCompletion` from every server advertising + /// `inlineCompletionProvider` (`automatic`: triggered by typing, not explicitly). + pub async fn inline_completion( + &self, + doc: DocId, + pos: Pos, + automatic: bool, + cancel: CancellationToken, + ) -> IdeResult> { + let (snap, targets) = self.doc_targets(doc, "inline completion", |c| c.has("inlineCompletionProvider")).await?; + let requests = targets.iter().map(|t| { + let mut params = snap.pos_params(t, pos); + params["context"] = json!({ "triggerKind": if automatic { 2 } else { 1 } }); + let cancel = &cancel; + async move { + let r = + self.call::<_, Value>(t, "textDocument/inlineCompletion", params, Some(cancel), COMPLETION_TIMEOUT); + (t, r.await) + } + }); + let mut out = Outcome::new(); + for (t, result) in join_all(requests).await { + match result { + Ok(v) => { + let conv = self.conv(t.enc()); + let items = convert::inline_completion_items(&conv, &snap.path, v, t.server.id); + out.value.get_or_insert_with(Vec::new).extend(items); + } + Err(e) => out.fail(e), + } + } + out.finish(Vec::new()) + } + pub async fn resolve_completion(&self, doc: DocId, item: CompletionItem) -> IdeResult { let snap = self.snapshot(doc)?; let Some(server) = snap.servers.iter().find(|s| s.id == item.server) else { return Ok(item) }; diff --git a/crates/ide-lsp/src/types.rs b/crates/ide-lsp/src/types.rs index 140a8282..0499d7c7 100644 --- a/crates/ide-lsp/src/types.rs +++ b/crates/ide-lsp/src/types.rs @@ -145,6 +145,21 @@ pub struct Command { pub arguments: Vec, } +/// One `textDocument/inlineCompletion` suggestion (ghost text at the caret). +#[derive(Clone, Debug, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct InlineCompletionItem { + /// Text that replaces `range`; snippets arrive as their plain text. + pub insert_text: String, + /// Replaced range; `None` inserts at the caret. + pub range: Option, + /// What the typed text is matched against while the suggestion stays shown (default: `insert_text`). + pub filter_text: Option, + /// Run after the suggestion is accepted. + pub command: Option, + pub server: ServerId, +} + #[derive(Clone, Debug, Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] pub struct CodeAction { diff --git a/crates/ide-lsp/tests/mock_server.rs b/crates/ide-lsp/tests/mock_server.rs index 562466f2..78ea902d 100644 --- a/crates/ide-lsp/tests/mock_server.rs +++ b/crates/ide-lsp/tests/mock_server.rs @@ -415,6 +415,42 @@ async fn completion_mapping_merge_and_resolve() { assert!(b.requested("completionItem/resolve").is_empty()); } +#[tokio::test(flavor = "multi_thread")] +async fn inline_completion_only_from_servers_that_advertise_it() { + let h = harness(&[ + ("plain", incremental_caps()), + ("ghost", json!({ "textDocumentSync": 2, "inlineCompletionProvider": {} })), + ("late", json!({ "textDocumentSync": 2 })), + ]); + let (doc, _) = h.open("a.mk", "let x = fo"); + for name in ["plain", "ghost", "late"] { + h.wait_status(name, ServerStatus::Ready).await; + } + let (plain, ghost) = (h.launcher.state("plain"), h.launcher.state("ghost")); + let init = &plain.requested("initialize")[0]; + assert_eq!(init["capabilities"]["textDocument"]["inlineCompletion"], json!({ "dynamicRegistration": true })); + ghost.respond( + "textDocument/inlineCompletion", + json!({ "items": [ + { "insertText": "foo(1)", "range": {"start":{"line":0,"character":8},"end":{"line":0,"character":10}}, + "command": { "title": "accepted", "command": "ghost.accepted", "arguments": [7] } }, + { "insertText": { "kind": "snippet", "value": "foo(${1:a}, $2)$0" }, "filterText": "fo" }, + { "insertText": "" } + ] }), + ); + let items = h.mgr.inline_completion(doc, Pos::new(0, 10), true, Default::default()).await.unwrap(); + assert_eq!(items.len(), 2); + assert_eq!(items[0].insert_text, "foo(1)"); + assert_eq!(items[0].range, Some(rng(0, 8, 0, 10))); + assert_eq!(items[0].command.as_ref().unwrap().command, "ghost.accepted"); + assert_eq!(items[1].insert_text, "foo(a, )"); + assert_eq!(items[1].filter_text.as_deref(), Some("fo")); + let req = &ghost.requested("textDocument/inlineCompletion")[0]; + assert_eq!(req["context"], json!({ "triggerKind": 2 })); + assert_eq!(req["position"], json!({ "line": 0, "character": 10 })); + assert!(plain.requested("textDocument/inlineCompletion").is_empty()); +} + #[tokio::test(flavor = "multi_thread")] async fn diagnostics_become_events_and_feed_code_actions() { let h = harness(&[("mock", incremental_caps())]); diff --git a/crates/ide-profiler/src/plan.rs b/crates/ide-profiler/src/plan.rs index 9a679e02..2345e8c9 100644 --- a/crates/ide-profiler/src/plan.rs +++ b/crates/ide-profiler/src/plan.rs @@ -233,6 +233,7 @@ mod tests { is_test: false, debug_adapter: None, allow_parallel: false, + extras: Default::default(), } } diff --git a/crates/ide-run/Cargo.toml b/crates/ide-run/Cargo.toml index 421e5cc4..c64c1773 100644 --- a/crates/ide-run/Cargo.toml +++ b/crates/ide-run/Cargo.toml @@ -14,6 +14,7 @@ specta.workspace = true toml.workspace = true toml_edit.workspace = true tokio.workspace = true +portable-pty.workspace = true parking_lot.workspace = true tracing.workspace = true diff --git a/crates/ide-run/src/config.rs b/crates/ide-run/src/config.rs index 4fc44368..e2f06708 100644 --- a/crates/ide-run/src/config.rs +++ b/crates/ide-run/src/config.rs @@ -12,9 +12,25 @@ //! is-test = false //! debug-adapter = "codelldb" //! allow-parallel = true +//! folder = "Servers" +//! params = { mode = "command" } +//! env-files = [".env"] +//! include-system-env = false +//! stdin-file = "input.txt" +//! output-file = "out/server.log" +//! before-launch = [{ kind = "build" }, { kind = "run", target = "user:migrate" }] +//! emulate-terminal = true +//! stop-command = "docker stop server" +//! +//! [[templates]] # per-type defaults for new configurations +//! kind = "npm" +//! env = { NODE_ENV = "development" } //! ``` +//! +//! Configurations not stored as project files live in the same format in the +//! workspace-local, git-ignored `run.local.toml`. -use crate::RunConfig; +use crate::{BeforeLaunchTask, RunConfig, RunExtras}; use ide_core::{IdeError, IdeResult, project_dir}; use serde::Deserialize; use std::collections::BTreeMap; @@ -23,16 +39,30 @@ use std::path::{Path, PathBuf}; use toml_edit::{Array, ArrayOfTables, DocumentMut, InlineTable, Item, Table, value}; const FILE: &str = "run.toml"; +const LOCAL_FILE: &str = "run.local.toml"; +const CONFIGURATIONS: &str = "configurations"; +const TEMPLATES: &str = "templates"; /// Where run configurations are written. pub(crate) fn config_path(root: &Path) -> PathBuf { project_dir::path(root, FILE) } +/// Workspace-local configurations ("Store as project file" off). +pub(crate) fn local_path(root: &Path) -> PathBuf { + project_dir::path(root, LOCAL_FILE) +} + #[derive(Deserialize)] struct FileDoc { #[serde(default)] configurations: Vec, + #[serde(default)] + templates: Vec, +} + +fn yes() -> bool { + true } #[derive(Deserialize)] @@ -54,6 +84,33 @@ struct FileConfig { debug_adapter: Option, #[serde(default, alias = "allowParallel", alias = "allow_parallel")] allow_parallel: bool, + #[serde(default)] + params: BTreeMap, + folder: Option, + #[serde(default = "yes", alias = "includeSystemEnv")] + include_system_env: bool, + #[serde(default, alias = "envFiles")] + env_files: Vec, + #[serde(alias = "stdinFile")] + stdin_file: Option, + #[serde(alias = "outputFile")] + output_file: Option, + #[serde(default, alias = "beforeLaunch")] + before_launch: Vec, + #[serde(default)] + compound: Vec, + #[serde(default, alias = "showOnStdout")] + show_on_stdout: bool, + #[serde(default, alias = "showOnStderr")] + show_on_stderr: bool, + #[serde(alias = "openToolWindow")] + open_tool_window: Option, + #[serde(default, alias = "focusToolWindow")] + focus_tool_window: bool, + #[serde(default, alias = "emulateTerminal")] + emulate_terminal: bool, + #[serde(alias = "stopCommand")] + stop_command: Option, } fn slug(s: &str) -> String { @@ -68,17 +125,22 @@ fn slug(s: &str) -> String { out.trim_matches('-').to_owned() } -pub(crate) fn load(root: &Path) -> IdeResult> { - let path = config_path(root); - let text = match fs::read_to_string(&path) { +fn read_doc(path: &Path) -> IdeResult> { + let text = match fs::read_to_string(path) { Ok(t) => t, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), Err(e) => return Err(e.into()), }; - let doc: FileDoc = - toml::from_str(&text).map_err(|e| IdeError::invalid(format!("{}: {}", path.display(), e.message())))?; - let mut out = Vec::with_capacity(doc.configurations.len()); - for (i, c) in doc.configurations.into_iter().enumerate() { + toml::from_str(&text).map(Some).map_err(|e| IdeError::invalid(format!("{}: {}", path.display(), e.message()))) +} + +fn non_empty(s: Option) -> Option { + s.filter(|s| !s.trim().is_empty()) +} + +fn convert(configs: Vec, local: bool) -> Vec { + let mut out = Vec::with_capacity(configs.len()); + for (i, c) in configs.into_iter().enumerate() { let name = c.name.filter(|n| !n.is_empty()).unwrap_or_else(|| { if c.command.is_empty() { format!("Configuration {}", i + 1) } else { c.command.clone() } }); @@ -95,11 +157,42 @@ pub(crate) fn load(root: &Path) -> IdeResult> { is_test: c.is_test, debug_adapter: c.debug_adapter.filter(|d| !d.is_empty()), allow_parallel: c.allow_parallel, + extras: RunExtras { + params: c.params, + folder: non_empty(c.folder), + local, + include_system_env: c.include_system_env, + env_files: c.env_files.into_iter().filter(|f| !f.trim().is_empty()).collect(), + stdin_file: non_empty(c.stdin_file), + output_file: non_empty(c.output_file), + before_launch: c.before_launch.into_iter().filter(|t| !t.kind.is_empty()).collect(), + compound: c.compound, + show_on_stdout: c.show_on_stdout, + show_on_stderr: c.show_on_stderr, + open_tool_window: c.open_tool_window, + focus_tool_window: c.focus_tool_window, + emulate_terminal: c.emulate_terminal, + stop_command: non_empty(c.stop_command), + }, }); } + out +} + +/// Project configurations (`run.toml`) followed by workspace-local ones (`run.local.toml`). +pub(crate) fn load(root: &Path) -> IdeResult> { + let mut out = read_doc(&config_path(root))?.map(|d| convert(d.configurations, false)).unwrap_or_default(); + if let Some(local) = read_doc(&local_path(root))? { + let ids: Vec = out.iter().map(|c| c.id.clone()).collect(); + out.extend(convert(local.configurations, true).into_iter().filter(|c| !ids.contains(&c.id))); + } Ok(out) } +pub(crate) fn load_templates(root: &Path) -> IdeResult> { + Ok(read_doc(&config_path(root))?.map(|d| convert(d.templates, false)).unwrap_or_default()) +} + fn to_table(c: &RunConfig) -> Table { let mut t = Table::new(); t.insert("id", value(&c.id)); @@ -128,12 +221,66 @@ fn to_table(c: &RunConfig) -> Table { if c.allow_parallel { t.insert("allow-parallel", value(true)); } + let x = &c.extras; + if let Some(folder) = &x.folder { + t.insert("folder", value(folder)); + } + if !x.params.is_empty() { + let mut params = InlineTable::new(); + for (k, v) in &x.params { + params.insert(k, v.into()); + } + t.insert("params", value(params)); + } + if !x.include_system_env { + t.insert("include-system-env", value(false)); + } + if !x.env_files.is_empty() { + t.insert("env-files", value(x.env_files.iter().collect::())); + } + if let Some(f) = &x.stdin_file { + t.insert("stdin-file", value(f)); + } + if let Some(f) = &x.output_file { + t.insert("output-file", value(f)); + } + if !x.before_launch.is_empty() { + let mut tasks = Array::new(); + for task in &x.before_launch { + let mut it = InlineTable::new(); + it.insert("kind", task.kind.as_str().into()); + if !task.target.is_empty() { + it.insert("target", task.target.as_str().into()); + } + tasks.push(it); + } + t.insert("before-launch", value(tasks)); + } + if !x.compound.is_empty() { + t.insert("compound", value(x.compound.iter().collect::())); + } + for (key, on) in [ + ("show-on-stdout", x.show_on_stdout), + ("show-on-stderr", x.show_on_stderr), + ("focus-tool-window", x.focus_tool_window), + ("emulate-terminal", x.emulate_terminal), + ] { + if on { + t.insert(key, value(true)); + } + } + if let Some(open) = x.open_tool_window { + t.insert("open-tool-window", value(open)); + } + if let Some(stop) = &x.stop_command { + t.insert("stop-command", value(stop)); + } t } -pub(crate) fn save(root: &Path, configs: &[RunConfig]) -> IdeResult<()> { - let path = config_path(root); - let existing = match fs::read_to_string(&path) { +/// Replaces the `key` array of tables in the file at `path`, keeping unrelated keys and comments. +fn write_array(path: &Path, key: &str, configs: &[&RunConfig]) -> IdeResult<()> { + let existing = match fs::read_to_string(path) { Ok(t) => Some(t), Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, Err(e) => return Err(e.into()), @@ -141,25 +288,65 @@ pub(crate) fn save(root: &Path, configs: &[RunConfig]) -> IdeResult<()> { if existing.is_none() && configs.is_empty() { return Ok(()); } - // Keep unrelated keys and comments; a file we cannot parse is replaced. + // A file we cannot parse is replaced. let mut doc = existing.as_deref().and_then(|t| t.parse::().ok()).unwrap_or_default(); - let mut array = ArrayOfTables::new(); - for c in configs { - array.push(to_table(c)); - } if configs.is_empty() { - doc.remove("configurations"); + doc.remove(key); } else { - doc.insert("configurations", Item::ArrayOfTables(array)); + let mut array = ArrayOfTables::new(); + for c in configs { + array.push(to_table(c)); + } + doc.insert(key, Item::ArrayOfTables(array)); + } + let text = doc.to_string(); + if existing.as_deref() == Some(text.as_str()) { + return Ok(()); } if let Some(dir) = path.parent() { fs::create_dir_all(dir)?; } let tmp = path.with_extension("toml.tmp"); - fs::write(&tmp, doc.to_string())?; - fs::rename(&tmp, &path).inspect_err(|_| { + fs::write(&tmp, text)?; + fs::rename(&tmp, path).inspect_err(|_| { let _ = fs::remove_file(&tmp); })?; Ok(()) } + +/// Keeps the workspace-local file out of version control (`.openidea/.gitignore`). +fn ignore_local(root: &Path) -> IdeResult<()> { + let entry = format!("/{LOCAL_FILE}"); + let file = project_dir::path(root, ".gitignore"); + let current = fs::read_to_string(&file).unwrap_or_default(); + if current.lines().any(|l| l.trim() == entry) { + return Ok(()); + } + let mut text = current; + if text.is_empty() { + text.push_str("# Per-user IDE state\n"); + } else if !text.ends_with('\n') { + text.push('\n'); + } + text.push_str(&entry); + text.push('\n'); + if let Some(dir) = file.parent() { + fs::create_dir_all(dir)?; + } + fs::write(file, text)?; + Ok(()) +} + +pub(crate) fn save(root: &Path, configs: &[RunConfig]) -> IdeResult<()> { + let (local, shared): (Vec<&RunConfig>, Vec<&RunConfig>) = configs.iter().partition(|c| c.extras.local); + write_array(&config_path(root), CONFIGURATIONS, &shared)?; + if !local.is_empty() { + ignore_local(root)?; + } + write_array(&local_path(root), CONFIGURATIONS, &local) +} + +pub(crate) fn save_templates(root: &Path, templates: &[RunConfig]) -> IdeResult<()> { + write_array(&config_path(root), TEMPLATES, &templates.iter().collect::>()) +} diff --git a/crates/ide-run/src/detect.rs b/crates/ide-run/src/detect.rs index cb4c2fe0..63becad6 100644 --- a/crates/ide-run/src/detect.rs +++ b/crates/ide-run/src/detect.rs @@ -38,6 +38,7 @@ fn config(id: impl Into, name: impl Into, kind: &str, command: & is_test: false, debug_adapter: None, allow_parallel: false, + extras: Default::default(), } } diff --git a/crates/ide-run/src/envfile.rs b/crates/ide-run/src/envfile.rs new file mode 100644 index 00000000..011a89e5 --- /dev/null +++ b/crates/ide-run/src/envfile.rs @@ -0,0 +1,159 @@ +//! `.env` files ("Paths to .env files" / EnvFile): `KEY=value` lines, `export` +//! prefixes, `#` comments, single- and double-quoted values (escapes in double +//! quotes) and `${VAR}` references to earlier variables or the base environment. + +use std::collections::BTreeMap; + +/// Parses `text`; `lookup` resolves `${VAR}` / `$VAR` not defined earlier in the file. +pub fn parse(text: &str, lookup: &dyn Fn(&str) -> Option) -> Vec<(String, String)> { + let mut out: Vec<(String, String)> = Vec::new(); + let mut lines = text.lines(); + while let Some(raw) = lines.next() { + let line = raw.trim_start_matches('\u{feff}').trim(); + if line.is_empty() || line.starts_with('#') { + continue; + } + let line = line.strip_prefix("export ").map(str::trim_start).unwrap_or(line); + let Some((key, rest)) = line.split_once('=') else { continue }; + let key = key.trim(); + if key.is_empty() || key.contains(char::is_whitespace) { + continue; + } + let rest = rest.trim_start(); + let value = if let Some(body) = rest.strip_prefix('\'') { + quoted(body, '\'', &mut lines).unwrap_or_else(|| rest.to_owned()) + } else if let Some(body) = rest.strip_prefix('"') { + let v = quoted(body, '"', &mut lines).unwrap_or_else(|| rest.to_owned()); + expand(&unescape(&v), &out, lookup) + } else { + let v = match rest.find(" #") { + Some(i) => &rest[..i], + None => rest, + }; + expand(v.trim_end(), &out, lookup) + }; + out.retain(|(k, _)| k != key); + out.push((key.to_owned(), value)); + } + out +} + +/// The body of a quoted value up to the closing `quote`, which may be on a later line. +fn quoted<'a>(first: &str, quote: char, lines: &mut impl Iterator) -> Option { + let mut text = first.to_owned(); + loop { + let mut escaped = false; + for (i, c) in text.char_indices() { + if quote == '"' && c == '\\' && !escaped { + escaped = true; + continue; + } + if c == quote && !escaped { + return Some(text[..i].to_owned()); + } + escaped = false; + } + text.push('\n'); + text.push_str(lines.next()?); + } +} + +fn unescape(s: &str) -> String { + let mut out = String::with_capacity(s.len()); + let mut chars = s.chars(); + while let Some(c) = chars.next() { + if c != '\\' { + out.push(c); + continue; + } + match chars.next() { + Some('n') => out.push('\n'), + Some('t') => out.push('\t'), + Some('r') => out.push('\r'), + Some(other) => out.push(other), + None => out.push('\\'), + } + } + out +} + +fn expand(value: &str, earlier: &[(String, String)], lookup: &dyn Fn(&str) -> Option) -> String { + let resolve = |name: &str| { + earlier + .iter() + .rev() + .find(|(k, _)| k == name) + .map(|(_, v)| v.clone()) + .or_else(|| lookup(name)) + .unwrap_or_default() + }; + let mut out = String::with_capacity(value.len()); + let bytes = value.as_bytes(); + let mut i = 0; + while i < value.len() { + if bytes[i] == b'$' { + if bytes.get(i + 1) == Some(&b'{') + && let Some(end) = value[i + 2..].find('}') + { + out.push_str(&resolve(&value[i + 2..i + 2 + end])); + i += end + 3; + continue; + } + let name_len = value[i + 1..].bytes().take_while(|b| b.is_ascii_alphanumeric() || *b == b'_').count(); + if name_len > 0 { + out.push_str(&resolve(&value[i + 1..i + 1 + name_len])); + i += name_len + 1; + continue; + } + } + let c = value[i..].chars().next().unwrap_or_default(); + out.push(c); + i += c.len_utf8(); + } + out +} + +/// Variables of every file in order (later files win); unreadable files are an error. +pub fn load( + paths: &[std::path::PathBuf], + base: &BTreeMap, +) -> std::io::Result> { + let mut vars: BTreeMap = BTreeMap::new(); + for path in paths { + let text = std::fs::read_to_string(path) + .map_err(|e| std::io::Error::new(e.kind(), format!("{}: {e}", path.display())))?; + let lookup = |name: &str| vars.get(name).or_else(|| base.get(name)).cloned(); + let parsed = parse(&text, &lookup); + vars.extend(parsed); + } + Ok(vars) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn none(_: &str) -> Option { + None + } + + #[test] + fn parses_common_forms() { + let text = "# comment\nA=1\nexport B = two words # trailing\nC='lit $A'\nD=\"x\\ny $A ${B}\"\nE=\"multi\nline\"\n\nbad line\nF=\n"; + let v = parse(text, &none); + let get = |k: &str| v.iter().find(|(n, _)| n == k).map(|(_, v)| v.as_str()); + assert_eq!(get("A"), Some("1")); + assert_eq!(get("B"), Some("two words")); + assert_eq!(get("C"), Some("lit $A")); + assert_eq!(get("D"), Some("x\ny 1 two words")); + assert_eq!(get("E"), Some("multi\nline")); + assert_eq!(get("F"), Some("")); + assert_eq!(v.len(), 6); + } + + #[test] + fn references_fall_back_to_base() { + let v = parse("P=$HOME/bin:${MISSING}x", &|n| (n == "HOME").then(|| "/h".to_owned())); + assert_eq!(v, [("P".to_owned(), "/h/bin:x".to_owned())]); + } +} diff --git a/crates/ide-run/src/lib.rs b/crates/ide-run/src/lib.rs index 55f95057..0a717609 100644 --- a/crates/ide-run/src/lib.rs +++ b/crates/ide-run/src/lib.rs @@ -2,6 +2,7 @@ mod config; mod detect; +pub mod envfile; pub mod junit; mod process; mod utf8; @@ -38,6 +39,113 @@ pub struct RunConfig { #[serde(default, skip_serializing_if = "std::ops::Not::not")] #[specta(optional)] pub allow_parallel: bool, + /// Options of user-defined configurations (all optional; old `run.toml` files load unchanged). + #[serde(flatten)] + pub extras: RunExtras, +} + +/// A step run before a configuration launches; a failing step aborts the launch. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct BeforeLaunchTask { + /// `run` (another configuration, `target` = its id) | `build` | `tool` (external tool id) | + /// `command` (a shell command line) | `toolWindow` (tool window id). + pub kind: String, + #[serde(default)] + pub target: String, +} + +fn yes() -> bool { + true +} + +fn is_true(b: &bool) -> bool { + *b +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct RunExtras { + /// Values of the configuration type's own form fields (the command line derives from them). + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + #[specta(optional)] + pub params: BTreeMap, + /// Folder in the Run/Debug Configurations tree. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[specta(optional)] + pub folder: Option, + /// Stored in the workspace (`.openidea/run.local.toml`, not shared) instead of `run.toml`. + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + #[specta(optional)] + pub local: bool, + /// Off: the process gets only the configured variables. + #[serde(default = "yes", skip_serializing_if = "is_true")] + #[specta(optional)] + pub include_system_env: bool, + /// `.env` files (relative to the project root) loaded before `env`, in order. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[specta(optional)] + pub env_files: Vec, + /// "Redirect input from": the process reads this file as stdin. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[specta(optional)] + pub stdin_file: Option, + /// "Save console output to file" (overwritten on each run). + #[serde(default, skip_serializing_if = "Option::is_none")] + #[specta(optional)] + pub output_file: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[specta(optional)] + pub before_launch: Vec, + /// Compound configurations: ids of the configurations started together. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[specta(optional)] + pub compound: Vec, + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + #[specta(optional)] + pub show_on_stdout: bool, + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + #[specta(optional)] + pub show_on_stderr: bool, + /// Overrides Settings › Run › "Show the Run tool window when a process starts". + #[serde(default, skip_serializing_if = "Option::is_none")] + #[specta(optional)] + pub open_tool_window: Option, + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + #[specta(optional)] + pub focus_tool_window: bool, + /// "Emulate terminal in output console": the process runs on a pseudo-terminal + /// (colours, line editing, full-screen programs); the console renders it as a terminal. + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + #[specta(optional)] + pub emulate_terminal: bool, + /// Shell command line run on Stop before the process is signalled (e.g. `docker stop `), + /// with the run's working directory and environment. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[specta(optional)] + pub stop_command: Option, +} + +impl Default for RunExtras { + fn default() -> Self { + Self { + params: BTreeMap::new(), + folder: None, + local: false, + include_system_env: true, + env_files: Vec::new(), + stdin_file: None, + output_file: None, + before_launch: Vec::new(), + compound: Vec::new(), + show_on_stdout: false, + show_on_stderr: false, + open_tool_window: None, + focus_tool_window: false, + emulate_terminal: false, + stop_command: None, + } + } } #[derive(Clone, Debug, Serialize, Deserialize, Type)] @@ -63,6 +171,8 @@ pub struct RunInfo { pub name: String, pub pid: Option, pub running: bool, + /// Runs on a pseudo-terminal: output is raw terminal data, input goes in keystroke by keystroke. + pub pty: bool, } /// Detects configs from package.json scripts, Cargo.toml targets, build.gradle(.kts), @@ -76,11 +186,26 @@ pub fn load_user_configs(root: &Path) -> IdeResult> { config::load(root) } -/// Writes `/.openidea/run.toml`, preserving unrelated content in the file. +/// Writes `/.openidea/run.toml` (and the workspace-local `run.local.toml` for +/// configurations not stored as project files), preserving unrelated content. pub fn save_user_configs(root: &Path, configs: &[RunConfig]) -> IdeResult<()> { config::save(root, configs) } +/// Configuration templates (per-type defaults for new configurations) from `run.toml`. +pub fn load_templates(root: &Path) -> IdeResult> { + config::load_templates(root) +} + +pub fn save_templates(root: &Path, templates: &[RunConfig]) -> IdeResult<()> { + config::save_templates(root, templates) +} + +/// The environment runs inherit (unless "Include system environment variables" is off). +pub fn system_env() -> BTreeMap { + std::env::vars().collect() +} + /// Path of the user run configuration file for `root`. pub fn user_configs_path(root: &Path) -> PathBuf { config::config_path(root) diff --git a/crates/ide-run/src/process.rs b/crates/ide-run/src/process.rs index 93c5b949..4874ede6 100644 --- a/crates/ide-run/src/process.rs +++ b/crates/ide-run/src/process.rs @@ -1,13 +1,15 @@ -//! Process lifecycle for run configurations: spawn, stream output, stdin, stop. +//! Process lifecycle for run configurations: spawn (on pipes, or on a pseudo-terminal +//! for "Emulate terminal in output console"), stream output, stdin, stop. use crate::utf8::OutputDecoder; use crate::{OutputChunk, RunConfig, RunInfo}; use ide_core::{IdeError, IdeEvent, IdeResult, RunId, SharedSink}; use parking_lot::Mutex; +use portable_pty::{CommandBuilder, MasterPty, PtySize, native_pty_system}; use std::collections::BTreeMap; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use std::process::{ChildStdin, Command, ExitStatus, Stdio}; +use std::process::{Command, ExitStatus, Stdio}; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{self, RecvTimeoutError, Sender}; @@ -20,15 +22,39 @@ const BATCH_BYTES: usize = 64 * 1024; const EXIT_DRAIN: Duration = Duration::from_millis(500); /// Delay between SIGINT and SIGKILL on [`ProcessManager::stop`]. const STOP_GRACE: Duration = Duration::from_secs(3); +/// Longest a configuration's stop command (e.g. `docker stop`) may take before signals follow. +const STOP_COMMAND_TIMEOUT: Duration = Duration::from_secs(60); /// Finished runs kept for [`ProcessManager::list`]. const MAX_FINISHED: usize = 20; +/// PTY size until the console reports its own. +const DEFAULT_PTY: (u16, u16) = (120, 30); + +type OutputFn = Arc; + +/// Environment of a launched process: the inherited one (unless cleared) plus `vars`. +#[derive(Clone, Debug, Default)] +struct LaunchEnv { + clear: bool, + vars: BTreeMap, +} + +/// A configuration's stop command and where it runs. +struct StopCommand { + line: String, + cwd: PathBuf, + env: LaunchEnv, +} struct Entry { id: RunId, config_id: String, name: String, pid: Option, - stdin: Mutex>, + stdin: Mutex>>, + /// The PTY master of a terminal-emulated run (resize; dropped on exit). + pty: Option>>>, + stop: Option, + output: OutputFn, exited: AtomicBool, stopping: AtomicBool, } @@ -41,6 +67,7 @@ impl Entry { name: self.name.clone(), pid: self.pid, running: !self.exited.load(Ordering::Acquire), + pty: self.pty.is_some(), } } } @@ -63,9 +90,14 @@ enum Stream { Stderr, } +enum Exit { + Pipes(std::io::Result), + Pty(std::io::Result), +} + enum Msg { Data(Stream, Vec), - Exited(std::io::Result), + Exited(Exit), } /// The program and arguments actually executed, plus the echoed command line. @@ -154,6 +186,23 @@ fn resolve_program(command: &str, cwd: &Path, env: &BTreeMap, wi path.to_path_buf() } +/// "Save console output to file": everything shown in the console is also written to `path`. +fn tee_output(path: &Path, on_output: OutputFn) -> IdeResult { + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; + } + let file = std::fs::File::create(path) + .map_err(|e| IdeError::invalid(format!("cannot write console output to {}: {e}", path.display())))?; + let file = Mutex::new(std::io::LineWriter::new(file)); + Ok(Arc::new(move |chunk: OutputChunk| { + let text = match &chunk { + OutputChunk::Stdout { text } | OutputChunk::Stderr { text } | OutputChunk::System { text } => text, + }; + let _ = file.lock().write_all(text.as_bytes()); + on_output(chunk); + })) +} + fn resolve_cwd(root: &Path, cwd: Option<&Path>) -> PathBuf { match cwd { Some(c) if c.is_absolute() => c.to_path_buf(), @@ -184,6 +233,24 @@ impl ProcessManager { return Err(IdeError::not_found(format!("working directory does not exist: {}", cwd.display()))); } let inv = invocation(config, &cwd); + let extras = &config.extras; + if extras.emulate_terminal && extras.stdin_file.is_some() { + return Err(IdeError::invalid("input cannot be redirected from a file when emulating a terminal")); + } + let env = launch_env(root, config)?; + let on_output: OutputFn = Arc::from(on_output); + let on_output = match extras.output_file.as_deref() { + Some(file) => tee_output(&resolve_cwd(root, Some(Path::new(file))), on_output)?, + None => on_output, + }; + let stop = extras.stop_command.as_deref().map(str::trim).filter(|l| !l.is_empty()).map(|line| StopCommand { + line: line.to_owned(), + cwd: cwd.clone(), + env: env.clone(), + }); + if extras.emulate_terminal { + return self.start_pty(config, &inv, &cwd, &env, stop, options, sink, on_output); + } let mut cmd = Command::new(&inv.program); if inv.raw_args { @@ -191,11 +258,21 @@ impl ProcessManager { } else { cmd.args(&inv.args); } - cmd.current_dir(&cwd).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()); - if matches!(config.kind.as_str(), "python" | "pytest") && !config.env.contains_key("PYTHONUNBUFFERED") { - cmd.env("PYTHONUNBUFFERED", "1"); - } - cmd.envs(&config.env); + cmd.current_dir(&cwd).stdout(Stdio::piped()).stderr(Stdio::piped()); + let redirected = match extras.stdin_file.as_deref() { + Some(file) => { + let path = resolve_cwd(root, Some(Path::new(file))); + let f = std::fs::File::open(&path) + .map_err(|e| IdeError::not_found(format!("cannot redirect input from {}: {e}", path.display())))?; + cmd.stdin(Stdio::from(f)); + true + } + None => { + cmd.stdin(Stdio::piped()); + false + } + }; + apply_env(&mut cmd, &env); #[cfg(unix)] { use std::os::unix::process::CommandExt; @@ -214,12 +291,19 @@ impl ProcessManager { })?; let id = RunId::next(); + let stdin: Option> = match child.stdin.take() { + Some(pipe) if !redirected => Some(Box::new(pipe)), + _ => None, + }; let entry = Arc::new(Entry { id, config_id: config.id.clone(), name: config.name.clone(), pid: Some(child.id()), - stdin: Mutex::new(child.stdin.take()), + stdin: Mutex::new(stdin), + pty: None, + stop, + output: on_output.clone(), exited: AtomicBool::new(false), stopping: AtomicBool::new(false), }); @@ -252,7 +336,7 @@ impl ProcessManager { .name(format!("openidea-run-wait-{}", id.0)) .spawn(move || { let status = child.wait(); - let _ = waiter_tx.send(Msg::Exited(status)); + let _ = waiter_tx.send(Msg::Exited(Exit::Pipes(status))); }) .map_err(|e| IdeError::internal(format!("failed to start waiter thread: {e}")))?; @@ -260,6 +344,94 @@ impl ProcessManager { Ok(entry.info()) } + /// [`Self::start`] on a pseudo-terminal: one merged output stream of raw terminal data. + #[allow(clippy::too_many_arguments)] + fn start_pty( + &self, + config: &RunConfig, + inv: &Invocation, + cwd: &Path, + env: &LaunchEnv, + stop: Option, + options: &StartOptions, + sink: SharedSink, + on_output: OutputFn, + ) -> IdeResult { + let pair = native_pty_system() + .openpty(pty_size(DEFAULT_PTY.0, DEFAULT_PTY.1)) + .map_err(|e| IdeError::internal(format!("openpty failed: {e}")))?; + let mut cmd = CommandBuilder::new(&inv.program); + cmd.args(&inv.args); + cmd.cwd(cwd); + if env.clear { + cmd.env_clear(); + } + cmd.env("TERM", "xterm-256color"); + cmd.env("COLORTERM", "truecolor"); + for (k, v) in &env.vars { + cmd.env(k, v); + } + let mut child = pair.slave.spawn_command(cmd).map_err(|e| { + let text = e.to_string(); + if text.contains("No such file") || text.contains("not found") { + IdeError::unavailable(format!("command not found: {}", config.command)) + } else { + IdeError::unavailable(format!("failed to start `{}`: {text}", config.command)) + } + })?; + // The child holds its own copy of the slave; dropping ours lets reads hit EOF on exit. + drop(pair.slave); + let reader = pair.master.try_clone_reader().map_err(|e| IdeError::internal(format!("pty reader: {e}")))?; + let writer = pair.master.take_writer().map_err(|e| IdeError::internal(format!("pty writer: {e}")))?; + + let id = RunId::next(); + let entry = Arc::new(Entry { + id, + config_id: config.id.clone(), + name: config.name.clone(), + pid: child.process_id(), + stdin: Mutex::new(Some(writer)), + pty: Some(Mutex::new(Some(pair.master))), + stop, + output: on_output.clone(), + exited: AtomicBool::new(false), + stopping: AtomicBool::new(false), + }); + self.register(entry.clone()); + on_output(OutputChunk::System { text: format!("{}\n", inv.display) }); + + let (tx, rx) = mpsc::channel(); + let result = spawn_reader(id, Stream::Stdout, reader, tx.clone()) + .and_then(|_| spawn_pump(rx, on_output, entry.clone(), sink, options.encoding.clone())); + if let Err(e) = result { + let _ = child.kill(); + let _ = child.wait(); + entry.exited.store(true, Ordering::Release); + return Err(IdeError::internal(format!("failed to start output threads: {e}"))); + } + std::thread::Builder::new() + .name(format!("openidea-run-wait-{}", id.0)) + .spawn(move || { + let status = child.wait(); + let _ = tx.send(Msg::Exited(Exit::Pty(status))); + }) + .map_err(|e| IdeError::internal(format!("failed to start waiter thread: {e}")))?; + tracing::debug!(%id, config = %config.id, pid = ?entry.pid, "run started on a pty"); + Ok(entry.info()) + } + + /// Resizes the pseudo-terminal of a terminal-emulated run. + pub fn resize(&self, id: RunId, cols: u16, rows: u16) -> IdeResult<()> { + let entry = self.entry(id)?; + let Some(pty) = &entry.pty else { return Err(IdeError::invalid("the process does not run in a terminal")) }; + match pty.lock().as_ref() { + Some(master) => { + master.resize(pty_size(cols, rows)).map_err(|e| IdeError::internal(format!("resize failed: {e}"))) + } + None => Ok(()), + } + } + fn register(&self, entry: Arc) { let mut runs = self.runs.lock(); runs.retain(|_, e| !(e.config_id == entry.config_id && e.exited.load(Ordering::Acquire))); @@ -293,9 +465,13 @@ impl ProcessManager { Ok(()) } - /// Closes the process's stdin (sends EOF). + /// Closes the process's stdin (sends EOF; on a terminal, types ^D). pub fn close_stdin(&self, id: RunId) -> IdeResult<()> { - self.entry(id)?.stdin.lock().take(); + let entry = self.entry(id)?; + if entry.pty.is_some() { + return self.write_stdin(id, "\u{4}"); + } + entry.stdin.lock().take(); Ok(()) } @@ -304,7 +480,8 @@ impl ProcessManager { self.stop_with_grace(id, STOP_GRACE) } - /// [`Self::stop`] with an explicit grace period before the kill. + /// [`Self::stop`] with an explicit grace period before the kill. A configuration's + /// stop command runs first; signals follow only if the process outlives it. pub fn stop_with_grace(&self, id: RunId, grace: Duration) -> IdeResult<()> { let entry = self.entry(id)?; if entry.exited.load(Ordering::Acquire) { @@ -316,19 +493,31 @@ impl ProcessManager { kill_tree(pid, true); return Ok(()); } - kill_tree(pid, false); + if entry.stop.is_none() { + kill_tree(pid, false); + } let entry = entry.clone(); std::thread::Builder::new() .name(format!("openidea-run-stop-{}", id.0)) .spawn(move || { - let deadline = Instant::now() + grace; - while Instant::now() < deadline { - if !group_alive(pid, &entry) { + let exits_within = |limit: Duration| { + let deadline = Instant::now() + limit; + while Instant::now() < deadline { + if !group_alive(pid, &entry) { + return true; + } + std::thread::sleep(Duration::from_millis(50)); + } + !group_alive(pid, &entry) + }; + if let Some(stop) = &entry.stop { + run_stop_command(stop, &entry.output); + if exits_within(grace) { return; } - std::thread::sleep(Duration::from_millis(50)); + kill_tree(pid, false); } - if group_alive(pid, &entry) { + if !exits_within(grace) { tracing::debug!(id = %entry.id, pid, "process ignored interrupt; killing"); kill_tree(pid, true); } @@ -354,6 +543,90 @@ impl Drop for ProcessManager { } } +/// The environment a configuration's process gets on top of the inherited one: `.env` +/// files, then the configured variables (later wins). +fn launch_env(root: &Path, config: &RunConfig) -> IdeResult { + let extras = &config.extras; + let mut vars = if extras.env_files.is_empty() { + BTreeMap::new() + } else { + let base: BTreeMap = + if extras.include_system_env { std::env::vars().collect() } else { BTreeMap::new() }; + let paths: Vec = extras.env_files.iter().map(|f| resolve_cwd(root, Some(Path::new(f)))).collect(); + crate::envfile::load(&paths, &base).map_err(|e| IdeError::not_found(format!("cannot read .env file: {e}")))? + }; + if matches!(config.kind.as_str(), "python" | "pytest") + && !config.env.contains_key("PYTHONUNBUFFERED") + && !vars.contains_key("PYTHONUNBUFFERED") + { + vars.insert("PYTHONUNBUFFERED".into(), "1".into()); + } + vars.extend(config.env.iter().map(|(k, v)| (k.clone(), v.clone()))); + Ok(LaunchEnv { clear: !extras.include_system_env, vars }) +} + +fn apply_env(cmd: &mut Command, env: &LaunchEnv) { + if env.clear { + cmd.env_clear(); + } + cmd.envs(&env.vars); +} + +/// Runs `stop.line` through the platform shell and waits for it (bounded), echoing it +/// and any error output to the console. +fn run_stop_command(stop: &StopCommand, output: &OutputFn) { + output(OutputChunk::System { text: format!("{}\n", stop.line) }); + let inv = shell_invocation(&stop.line, cfg!(windows), std::env::var("COMSPEC").ok()); + let mut cmd = Command::new(&inv.program); + if inv.raw_args { + add_raw_args(&mut cmd, &inv.args); + } else { + cmd.args(&inv.args); + } + cmd.current_dir(&stop.cwd).stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::piped()); + apply_env(&mut cmd, &stop.env); + ide_core::process::no_window(&mut cmd); + let mut child = match cmd.spawn() { + Ok(c) => c, + Err(e) => { + output(OutputChunk::Stderr { text: format!("Cannot run the stop command: {e}\n") }); + return; + } + }; + let stderr = child.stderr.take().map(|mut pipe| { + std::thread::spawn(move || { + let mut buf = Vec::new(); + let _ = pipe.read_to_end(&mut buf); + String::from_utf8_lossy(&buf).into_owned() + }) + }); + let deadline = Instant::now() + STOP_COMMAND_TIMEOUT; + let status = loop { + match child.try_wait() { + Ok(Some(status)) => break Some(status), + Ok(None) if Instant::now() < deadline => std::thread::sleep(Duration::from_millis(50)), + _ => { + let _ = child.kill(); + let _ = child.wait(); + break None; + } + } + }; + let err = stderr.and_then(|h| h.join().ok()).unwrap_or_default(); + if !status.is_some_and(|s| s.success()) && !err.trim().is_empty() { + output(OutputChunk::Stderr { text: format!("{}\n", err.trim_end()) }); + } +} + +fn pty_size(cols: u16, rows: u16) -> PtySize { + PtySize { + cols: if cols == 0 { DEFAULT_PTY.0 } else { cols }, + rows: if rows == 0 { DEFAULT_PTY.1 } else { rows }, + pixel_width: 0, + pixel_height: 0, + } +} + #[cfg(unix)] fn group_alive(pid: u32, _entry: &Entry) -> bool { // Signal 0 probes the group; it fails with ESRCH once every member is gone. @@ -440,7 +713,7 @@ fn chunk(stream: Stream, text: String) -> OutputChunk { /// Merges both streams in arrival order, batching consecutive output of the same stream. fn spawn_pump( rx: mpsc::Receiver, - on_output: Box, + on_output: OutputFn, entry: Arc, sink: SharedSink, encoding: Option, @@ -451,7 +724,7 @@ fn spawn_pump( let mut pending = String::new(); let mut pending_stream = Stream::Stdout; let mut batch_started: Option = None; - let mut status: Option> = None; + let mut status: Option = None; let mut drain_deadline: Option = None; let flush = |pending: &mut String, stream: Stream, started: &mut Option| { @@ -512,6 +785,9 @@ fn spawn_pump( let (code, message) = describe_exit(status); on_output(OutputChunk::System { text: format!("\n{message}\n") }); entry.stdin.lock().take(); + if let Some(pty) = &entry.pty { + pty.lock().take(); + } entry.exited.store(true, Ordering::Release); tracing::debug!(id = %entry.id, ?code, "run exited"); sink.emit(IdeEvent::RunExited { run: entry.id, code }); @@ -542,9 +818,9 @@ fn signal_name(sig: i32) -> &'static str { } /// IDEA wording: `Process finished with exit code 130 (interrupted by signal 2:SIGINT)`. -fn describe_exit(status: Option>) -> (Option, String) { +fn describe_exit(status: Option) -> (Option, String) { match status { - Some(Ok(s)) => { + Some(Exit::Pipes(Ok(s))) => { if let Some(code) = s.code() { return (Some(code), format!("Process finished with exit code {code}")); } @@ -552,23 +828,55 @@ fn describe_exit(status: Option>) -> (Option, S { use std::os::unix::process::ExitStatusExt; if let Some(sig) = s.signal() { - let code = 128 + sig; - return ( - Some(code), - format!( - "Process finished with exit code {code} (interrupted by signal {sig}:{})", - signal_name(sig) - ), - ); + return signalled(sig); } } (None, "Process finished".to_owned()) } - Some(Err(e)) => (None, format!("Process finished with unknown exit status: {e}")), + Some(Exit::Pty(Ok(s))) => match s.signal() { + None => { + let code = s.exit_code() as i32; + (Some(code), format!("Process finished with exit code {code}")) + } + Some(name) => match signal_number(name) { + Some(sig) => signalled(sig), + None => (None, format!("Process finished ({name})")), + }, + }, + Some(Exit::Pipes(Err(e)) | Exit::Pty(Err(e))) => { + (None, format!("Process finished with unknown exit status: {e}")) + } None => (None, "Process finished".to_owned()), } } +#[cfg(unix)] +fn signalled(sig: i32) -> (Option, String) { + let code = 128 + sig; + (Some(code), format!("Process finished with exit code {code} (interrupted by signal {sig}:{})", signal_name(sig))) +} + +#[cfg(not(unix))] +fn signalled(sig: i32) -> (Option, String) { + (Some(128 + sig), format!("Process finished with exit code {}", 128 + sig)) +} + +/// The number of a signal from the `strsignal` description portable-pty reports. +#[cfg(unix)] +fn signal_number(description: &str) -> Option { + (1..32).find(|&sig| { + // SAFETY: `strsignal` returns a pointer to a NUL-terminated C string that stays + // valid until the next call on this thread; it is copied before that. + let name = unsafe { libc::strsignal(sig) }; + !name.is_null() && unsafe { std::ffi::CStr::from_ptr(name) }.to_string_lossy() == description + }) +} + +#[cfg(not(unix))] +fn signal_number(_description: &str) -> Option { + None +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/ide-run/src/wrap.rs b/crates/ide-run/src/wrap.rs index 3c00c97b..0e8aabc5 100644 --- a/crates/ide-run/src/wrap.rs +++ b/crates/ide-run/src/wrap.rs @@ -261,6 +261,7 @@ mod tests { is_test: false, debug_adapter: None, allow_parallel: false, + extras: Default::default(), } } diff --git a/crates/ide-run/tests/config.rs b/crates/ide-run/tests/config.rs index 74344b02..b4bb34ac 100644 --- a/crates/ide-run/tests/config.rs +++ b/crates/ide-run/tests/config.rs @@ -17,6 +17,7 @@ fn sample() -> Vec { is_test: false, debug_adapter: Some("codelldb".into()), allow_parallel: true, + extras: Default::default(), }, RunConfig { id: "user:tests".into(), @@ -30,6 +31,7 @@ fn sample() -> Vec { is_test: true, debug_adapter: None, allow_parallel: false, + extras: Default::default(), }, ] } @@ -112,3 +114,58 @@ fn invalid_file_is_reported() { assert_eq!(err.kind, ide_core::error::ErrorKind::InvalidInput); assert!(err.message.contains("run.toml")); } + +#[test] +fn extras_round_trip_and_local_file() { + use ide_run::{BeforeLaunchTask, load_templates, save_templates}; + let dir = tempfile::tempdir().unwrap(); + let mut shared = sample().remove(0); + shared.extras.folder = Some("Servers".into()); + shared.extras.params = BTreeMap::from([("mode".into(), "command".into())]); + shared.extras.include_system_env = false; + shared.extras.env_files = vec![".env".into(), "local.env".into()]; + shared.extras.stdin_file = Some("in.txt".into()); + shared.extras.output_file = Some("out.log".into()); + shared.extras.before_launch = vec![ + BeforeLaunchTask { kind: "build".into(), target: String::new() }, + BeforeLaunchTask { kind: "run".into(), target: "user:migrate".into() }, + ]; + shared.extras.show_on_stderr = true; + shared.extras.open_tool_window = Some(false); + shared.extras.focus_tool_window = true; + shared.extras.emulate_terminal = true; + shared.extras.stop_command = Some("docker stop server".into()); + let mut local = sample().remove(1); + local.extras.local = true; + let mut compound = sample().remove(1); + compound.id = "user:all".into(); + compound.name = "All".into(); + compound.kind = "compound".into(); + compound.command = String::new(); + compound.extras.compound = vec!["user:server".into(), "user:tests".into()]; + let configs = vec![shared, compound, local]; + save_user_configs(dir.path(), &configs).unwrap(); + assert_eq!(load_user_configs(dir.path()).unwrap(), configs); + + let project = fs::read_to_string(user_configs_path(dir.path())).unwrap(); + assert!(!project.contains("\"user:tests\"\n"), "{project}"); + let private = fs::read_to_string(dir.path().join(".openidea/run.local.toml")).unwrap(); + assert!(private.contains("id = \"user:tests\""), "{private}"); + let ignore = fs::read_to_string(dir.path().join(".openidea/.gitignore")).unwrap(); + assert!(ignore.lines().any(|l| l == "/run.local.toml"), "{ignore}"); + + // Making it a project file moves it back. + let mut moved = configs.clone(); + moved[2].extras.local = false; + save_user_configs(dir.path(), &moved).unwrap(); + assert_eq!(load_user_configs(dir.path()).unwrap(), moved); + assert!(!fs::read_to_string(dir.path().join(".openidea/run.local.toml")).unwrap().contains("user:tests")); + + // Templates share run.toml without disturbing configurations. + let mut template = sample().remove(1); + template.id = "template:npm".into(); + template.kind = "npm".into(); + save_templates(dir.path(), std::slice::from_ref(&template)).unwrap(); + assert_eq!(load_templates(dir.path()).unwrap(), vec![template]); + assert_eq!(load_user_configs(dir.path()).unwrap(), moved); +} diff --git a/crates/ide-run/tests/process.rs b/crates/ide-run/tests/process.rs index bd9d51c4..946edfc1 100644 --- a/crates/ide-run/tests/process.rs +++ b/crates/ide-run/tests/process.rs @@ -43,6 +43,7 @@ fn cfg(id: &str, command: &str, args: &[&str]) -> RunConfig { is_test: false, debug_adapter: None, allow_parallel: false, + extras: Default::default(), } } @@ -273,3 +274,108 @@ fn exit_event_goes_to_the_starters_sink_only() { assert!(other_events.try_recv().is_err()); assert!(h.events.try_recv().is_err()); } + +#[test] +fn env_files_system_env_and_redirects() { + let h = harness(); + let root = h.root.path(); + std::fs::write(root.join(".env"), "FROM_FILE=file\nOVERRIDDEN=file\n").unwrap(); + std::fs::write(root.join("input.txt"), "from file\n").unwrap(); + let mut config = cfg( + "t:extras", + "/bin/sh", + &["-c", "printf '%s|%s|%s|' \"$FROM_FILE\" \"$OVERRIDDEN\" \"${HOME:-unset}\"; cat"], + ); + config.env.insert("OVERRIDDEN".into(), "config".into()); + config.extras.env_files = vec![".env".into()]; + config.extras.include_system_env = false; + config.extras.stdin_file = Some("input.txt".into()); + config.extras.output_file = Some("logs/out.log".into()); + let (id, chunks) = start(&h, &config); + // stdin comes from the file: writing to it is refused. + assert!(h.pm.write_stdin(id, "x").is_err()); + assert_eq!(wait_exit(&h, id, Duration::from_secs(10)), Some(0)); + assert_eq!(text(&chunks, stdout), "file|config|unset|from file\n"); + let saved = std::fs::read_to_string(root.join("logs/out.log")).unwrap(); + assert!(saved.contains("file|config|unset|from file\n"), "{saved}"); + assert!(saved.contains("Process finished with exit code 0"), "{saved}"); + + // With the system environment, HOME is inherited. + config.extras.include_system_env = true; + config.extras.stdin_file = None; + config.extras.output_file = None; + config.args = vec!["-c".into(), "printf '%s' \"${HOME:+set}\"".into()]; + let (id, chunks) = start(&h, &config); + assert_eq!(wait_exit(&h, id, Duration::from_secs(10)), Some(0)); + assert_eq!(text(&chunks, stdout), "set"); + + config.extras.env_files = vec!["missing.env".into()]; + assert!(h.pm.start(root, &config, &StartOptions::default(), h.sink.clone(), Box::new(|_| {})).is_err()); +} + +#[test] +fn emulated_terminal_is_a_tty_with_input_resize_and_exit_code() { + let h = harness(); + let script = "[ -t 1 ] && echo tty-yes; echo \"term=$TERM\"; read line; echo \"got:$line\"; stty size; exit 3"; + let mut config = cfg("t:pty", "/bin/sh", &["-c", script]); + config.extras.emulate_terminal = true; + let (id, chunks) = start(&h, &config); + assert!(h.pm.list().iter().any(|r| r.id == id && r.pty)); + wait_for(&chunks, "term=xterm-256color"); + h.pm.resize(id, 100, 40).unwrap(); + h.pm.write_stdin(id, "hello\r").unwrap(); + let out = wait_for(&chunks, "40 100"); + assert!(out.contains("tty-yes"), "{out:?}"); + assert!(out.contains("got:hello"), "{out:?}"); + assert_eq!(wait_exit(&h, id, Duration::from_secs(10)), Some(3)); + assert!(text(&chunks, system).contains("Process finished with exit code 3")); + // Stdout and stderr share the terminal. + assert_eq!(text(&chunks, stderr), ""); +} + +#[test] +fn emulated_terminal_stop_reports_the_signal() { + let h = harness(); + let mut config = cfg("t:pty-stop", "/bin/sh", &["-c", "echo ready; sleep 30"]); + config.extras.emulate_terminal = true; + let (id, chunks) = start(&h, &config); + wait_for(&chunks, "ready"); + h.pm.stop(id).unwrap(); + let code = wait_exit(&h, id, Duration::from_secs(10)); + assert_eq!(code, Some(130), "{:?}", text(&chunks, system)); + assert!(text(&chunks, system).contains("SIGINT")); +} + +#[test] +fn emulated_terminal_refuses_input_redirection() { + let h = harness(); + let mut config = cfg("t:pty-stdin", "/bin/cat", &[]); + config.extras.emulate_terminal = true; + config.extras.stdin_file = Some("input.txt".into()); + assert!(h.pm.start(h.root.path(), &config, &StartOptions::default(), h.sink.clone(), Box::new(|_| {})).is_err()); +} + +#[test] +fn pipe_runs_cannot_be_resized() { + let h = harness(); + let (id, _chunks) = start(&h, &cfg("t:pipes", "/bin/sh", &["-c", "sleep 5"])); + assert!(!h.pm.list().iter().find(|r| r.id == id).unwrap().pty); + assert!(h.pm.resize(id, 80, 24).is_err()); + h.pm.stop(id).unwrap(); + wait_exit(&h, id, Duration::from_secs(10)); +} + +#[test] +fn stop_command_runs_first_with_the_run_environment() { + let h = harness(); + let mut config = cfg("t:stop-cmd", "/bin/sh", &["-c", "echo ready; sleep 30"]); + config.env.insert("OPENIDEA_MARKER".into(), "stopped".into()); + config.extras.stop_command = Some("touch \"$OPENIDEA_MARKER\"".into()); + let (id, chunks) = start(&h, &config); + wait_for(&chunks, "ready"); + h.pm.stop_with_grace(id, Duration::from_millis(200)).unwrap(); + // `sleep` ignores nothing: the signals that follow the stop command end it. + assert_eq!(wait_exit(&h, id, Duration::from_secs(10)), Some(130)); + assert!(h.root.path().join("stopped").exists(), "stop command did not run"); + assert!(text(&chunks, system).contains("touch \"$OPENIDEA_MARKER\"")); +} diff --git a/crates/ide-sdk/Cargo.toml b/crates/ide-sdk/Cargo.toml index cce2ab5b..028f233f 100644 --- a/crates/ide-sdk/Cargo.toml +++ b/crates/ide-sdk/Cargo.toml @@ -10,6 +10,7 @@ dunce.workspace = true ide-core.workspace = true ide-language.workspace = true ide-run.workspace = true +ide-vfs.workspace = true serde.workspace = true serde_json.workspace = true specta.workspace = true diff --git a/crates/ide-sdk/src/apply.rs b/crates/ide-sdk/src/apply.rs index 9e956450..490defe0 100644 --- a/crates/ide-sdk/src/apply.rs +++ b/crates/ide-sdk/src/apply.rs @@ -32,12 +32,12 @@ pub fn python_interpreter(root: &Path, s: &Settings) -> Option { } /// The configured `node` executable's directory. -fn node_dir(root: &Path, s: &Settings) -> Option { +pub(crate) fn node_dir(root: &Path, s: &Settings) -> Option { setting_path(root, &s.languages.node.interpreter).and_then(|p| p.parent().map(Path::to_path_buf)) } /// The configured Rust toolchain's `bin` directory and rustup toolchain name. -fn rust_bin(root: &Path, s: &Settings) -> Option<(PathBuf, Option)> { +pub(crate) fn rust_bin(root: &Path, s: &Settings) -> Option<(PathBuf, Option)> { let dir = setting_path(root, &s.languages.rust.toolchain)?; let bin = if dir.join("bin").join(exe("rustc")).is_file() || dir.join("bin").join(exe("cargo")).is_file() { dir.join("bin") @@ -52,7 +52,7 @@ fn rust_bin(root: &Path, s: &Settings) -> Option<(PathBuf, Option)> { Some((bin, toolchain)) } -fn goroot(root: &Path, s: &Settings) -> Option { +pub(crate) fn goroot(root: &Path, s: &Settings) -> Option { setting_path(root, &s.languages.go.goroot) } diff --git a/crates/ide-sdk/src/exec.rs b/crates/ide-sdk/src/exec.rs index 267aa91d..fd3806ae 100644 --- a/crates/ide-sdk/src/exec.rs +++ b/crates/ide-sdk/src/exec.rs @@ -44,9 +44,25 @@ pub(crate) fn which(name: &str) -> Option { /// Runs `program args` and returns trimmed stdout (stderr when stdout is empty) /// on success; `None` on failure or after `timeout`. pub(crate) fn query(program: &Path, args: &[&str], timeout: Duration) -> Option { - let mut child = ide_core::process::no_window(&mut Command::new(program)) + query_in(program, args, None, &[], timeout) +} + +/// [`query`] in `cwd` with extra environment variables. +pub(crate) fn query_in( + program: &Path, + args: &[&str], + cwd: Option<&Path>, + env: &[(&str, &str)], + timeout: Duration, +) -> Option { + let mut cmd = Command::new(program); + if let Some(cwd) = cwd { + cmd.current_dir(cwd); + } + let mut child = ide_core::process::no_window(&mut cmd) .args(args) .env("PATH", search_path()) + .envs(env.iter().copied()) .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) diff --git a/crates/ide-sdk/src/lib.rs b/crates/ide-sdk/src/lib.rs index 296a7502..1068f19e 100644 --- a/crates/ide-sdk/src/lib.rs +++ b/crates/ide-sdk/src/lib.rs @@ -9,6 +9,7 @@ pub mod apply; mod discover; mod exec; pub mod java; +pub mod libraries; pub mod locate; pub mod lombok; pub mod python; diff --git a/crates/ide-sdk/src/libraries.rs b/crates/ide-sdk/src/libraries.rs new file mode 100644 index 00000000..08b1161b --- /dev/null +++ b/crates/ide-sdk/src/libraries.rs @@ -0,0 +1,469 @@ +//! "External Libraries" of a project (IDEA's library / SDK roots in the +//! Project view): the folders holding the sources of the project's SDKs and +//! dependencies — the Python interpreter's standard library and site-packages, +//! the JDK, `node_modules` packages, Cargo crates and the Rust standard +//! library, GOROOT and the Go module cache. +//! +//! Each ecosystem is detected only when the project has its manifest, and its +//! result is cached until the manifests (or the relevant settings) change. +//! +//! Archives are listed as browsable roots (`!`, see `ide_vfs`): the +//! JDK's `src.zip` and the Maven / Gradle dependency jars (sources jars preferred). + +use crate::exe; +use crate::exec::{query, query_in, which}; +use ide_core::settings::Settings; +use serde::{Deserialize, Serialize}; +use specta::Type; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::sync::{LazyLock, Mutex}; +use std::time::{Duration, SystemTime}; + +/// One SDK or library with its source folders (`< Python 3.12 (.venv) >`). +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct LibraryGroup { + /// Display name, e.g. `Python 3.12 (.venv)`. + pub name: String, + /// `python` | `jdk` | `nodeModules` | `nodeCore` | `rust` | `cargo` | `go` | `goModules` | `maven` | `gradle`. + pub kind: String, + pub roots: Vec, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct LibraryRoot { + /// Folder name, e.g. `python3.12` or `site-packages`, the package (`serde 1.0.200`) + /// or the archive (`src.zip`, `guava-33.0.0-jre-sources.jar`). + pub name: String, + /// A folder, or an archive's root (`!`). + pub path: ide_core::IpcPath, +} + +const QUERY_TIMEOUT: Duration = Duration::from_secs(10); +/// `cargo metadata` / `go list` load the whole dependency graph. +const GRAPH_TIMEOUT: Duration = Duration::from_secs(60); + +/// Prints the interpreter's library folders as JSON: `[stdlib, platstdlib, purelib, platlib]`. +const PYTHON_PATHS: &str = "import json,sysconfig;p=sysconfig.get_paths();\ +print(json.dumps([p.get(k) for k in ('stdlib','platstdlib','purelib','platlib')]))"; + +const JAVA_MANIFESTS: &[&str] = + &["pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"]; +const NODE_MANIFESTS: &[&str] = &["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "bun.lockb"]; +const CARGO_MANIFESTS: &[&str] = &["Cargo.toml", "Cargo.lock", "rust-toolchain", "rust-toolchain.toml"]; +const GO_MANIFESTS: &[&str] = &["go.mod", "go.sum", "go.work"]; + +/// A Maven / Gradle dependency of the project, from the build model. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BuildDependency { + /// Resolved by Gradle (else Maven): looked up in the Gradle cache first. + pub gradle: bool, + pub group: String, + pub artifact: String, + pub version: String, +} + +/// The project's library groups; empty when no SDK is configured. Runs the +/// toolchains (in parallel), so call it off the UI / command thread. +/// `dependencies` (from the build model) are listed when their jars are in the local repositories. +pub fn project_libraries(root: &Path, settings: &Settings, dependencies: &[BuildDependency]) -> Vec { + type Detect = fn(&Path, &Settings) -> Vec; + let detectors: [Detect; 5] = [python_groups, jdk_groups, node_groups, rust_groups, go_groups]; + std::thread::scope(|s| { + let handles: Vec<_> = detectors.iter().map(|d| s.spawn(move || d(root, settings))).collect(); + let deps = s.spawn(|| dependency_groups(root, settings, dependencies)); + handles.into_iter().chain([deps]).flat_map(|h| h.join().unwrap_or_default()).collect() + }) +} + +// ------------------------------------------------------------------ cache + +/// Size and modification time of each manifest (`None`: missing). +type Stamp = Vec<(PathBuf, Option<(SystemTime, u64)>)>; + +struct Cached { + /// Settings the result depends on. + key: String, + stamp: Stamp, + groups: Vec, +} + +static CACHE: LazyLock>> = LazyLock::new(Default::default); + +fn stamp(files: &[PathBuf]) -> Stamp { + files + .iter() + .map(|f| { + let meta = std::fs::metadata(f).ok().and_then(|m| Some((m.modified().ok()?, m.len()))); + (f.clone(), meta) + }) + .collect() +} + +/// `detect()` unless the last result for `(root, ecosystem)` was computed with +/// the same `key` and unchanged `manifests`. +pub(crate) fn cached( + root: &Path, + ecosystem: &'static str, + key: String, + manifests: &[PathBuf], + detect: impl FnOnce() -> Vec, +) -> Vec { + let id = (root.to_path_buf(), ecosystem); + let now = stamp(manifests); + if let Some(c) = CACHE.lock().unwrap_or_else(|e| e.into_inner()).get(&id) + && c.key == key + && c.stamp == now + { + return c.groups.clone(); + } + let groups = detect(); + CACHE.lock().unwrap_or_else(|e| e.into_inner()).insert(id, Cached { key, stamp: now, groups: groups.clone() }); + groups +} + +/// `names` below `root` when at least one of them exists (the project uses that ecosystem). +fn manifests(root: &Path, names: &[&str]) -> Option> { + let files: Vec = names.iter().map(|n| root.join(n)).collect(); + files.iter().any(|f| f.is_file()).then_some(files) +} + +fn group(name: impl Into, kind: &str, roots: Vec) -> Option { + (!roots.is_empty()).then(|| LibraryGroup { name: name.into(), kind: kind.into(), roots }) +} + +/// A configured toolchain `bin` dir's `name`, else `name` on the search path. +fn tool(bin: Option, name: &str) -> Option { + bin.map(|b| b.join(exe(name))).filter(|p| p.is_file()).or_else(|| which(name)) +} + +// ------------------------------------------------------------------ python + +fn python_groups(root: &Path, s: &Settings) -> Vec { + let Some(python) = crate::apply::python_interpreter(root, s) else { return Vec::new() }; + let mut files = vec![python.clone()]; + files.extend(crate::venv_of(&python).map(|v| v.join("pyvenv.cfg"))); + cached(root, "python", python.display().to_string(), &files, || { + python_libraries(&python, Some(root)).into_iter().collect() + }) +} + +/// Standard library and site-packages folders of a Python interpreter. +pub fn python_libraries(python: &Path, root: Option<&Path>) -> Option { + let out = query(python, &["-c", PYTHON_PATHS], QUERY_TIMEOUT)?; + let name = crate::discover::python_sdk(python, "system", root).map(|s| s.name).unwrap_or_else(|| "Python".into()); + group(name, "python", library_roots(parse_python_paths(&out))) +} + +pub(crate) fn parse_python_paths(out: &str) -> Vec { + let json = out.lines().rev().find(|l| l.trim_start().starts_with('[')).unwrap_or("[]"); + serde_json::from_str::>>(json) + .unwrap_or_default() + .into_iter() + .flatten() + .map(PathBuf::from) + .collect() +} + +/// Existing folders among `paths`, deduplicated (also through symlinks), in order. +pub(crate) fn library_roots(paths: Vec) -> Vec { + named_roots( + paths + .into_iter() + .map(|p| { + (p.file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_else(|| p.display().to_string()), p) + }) + .collect(), + ) +} + +/// Existing folders among `(name, path)`, deduplicated (also through symlinks), in order. +pub(crate) fn named_roots(roots: Vec<(String, PathBuf)>) -> Vec { + let mut seen = std::collections::HashSet::new(); + roots + .into_iter() + .filter(|(_, p)| p.is_dir() && seen.insert(dunce::canonicalize(p).unwrap_or_else(|_| p.clone()))) + .map(|(name, p)| LibraryRoot { name, path: p.into() }) + .collect() +} + +// ------------------------------------------------------------------ jdk + +fn jdk_groups(root: &Path, s: &Settings) -> Vec { + let Some(files) = manifests(root, JAVA_MANIFESTS) else { return Vec::new() }; + cached(root, "jdk", s.languages.java.jdk.clone(), &files, || { + let jdk = crate::apply::project_jdk(root, s).or_else(|| { + crate::java::pick_jdk(&crate::java::installed_jdks(), crate::java::project_java_version(root)).cloned() + }); + jdk.and_then(|j| jdk_library(&j)).into_iter().collect() + }) +} + +/// The JDK home and its sources (`lib/src.zip`, `src.zip` in JDK 8) as library roots. +pub(crate) fn jdk_library(jdk: &crate::java::Jdk) -> Option { + let name = jdk_name(&jdk.home).unwrap_or_else(|| format!("JDK {}", jdk.major)); + let mut roots = named_roots(vec![(name.clone(), jdk.home.clone())]); + if let Some(src) = ["lib/src.zip", "src.zip"].iter().map(|r| jdk.home.join(r)).find(|p| p.is_file()) { + roots.push(LibraryRoot { name: "src.zip".into(), path: ide_vfs::archive_root(&src).into() }); + } + group(name, "jdk", roots) +} + +/// `temurin-21` for `…/temurin-21.jdk/Contents/Home` (macOS bundles) or `…/temurin-21`. +pub(crate) fn jdk_name(home: &Path) -> Option { + let dir = if home.ends_with("Contents/Home") { home.parent()?.parent()? } else { home }; + let name = dir.file_name()?.to_string_lossy(); + Some(name.strip_suffix(".jdk").unwrap_or(&name).to_owned()) +} + +// ------------------------------------------------------------------ maven / gradle + +fn dependency_groups(root: &Path, s: &Settings, deps: &[BuildDependency]) -> Vec { + if deps.is_empty() { + return Vec::new(); + } + let maven_repo = crate::apply::maven_local_repository(root, s).or_else(default_maven_repository); + let gradle_home = gradle_user_home(); + let files: Vec = JAVA_MANIFESTS.iter().map(|n| root.join(n)).collect(); + let key = format!("{maven_repo:?}\n{gradle_home:?}\n{deps:?}"); + cached(root, "buildDependencies", key, &files, || { + dependency_libraries(deps, maven_repo.as_deref(), gradle_home.as_deref()) + }) +} + +/// `~/.m2/repository` (Settings › Maven › Local repository overrides it). +fn default_maven_repository() -> Option { + Some(crate::home_dir()?.join(".m2/repository")) +} + +/// `$GRADLE_USER_HOME`, else `~/.gradle`. +fn gradle_user_home() -> Option { + std::env::var_os("GRADLE_USER_HOME").map(PathBuf::from).or_else(|| Some(crate::home_dir()?.join(".gradle"))) +} + +/// One `Maven: g:a:v` / `Gradle: g:a:v` group per dependency whose jar is in a +/// local repository, its root being the jar (the `-sources.jar` when downloaded). +pub(crate) fn dependency_libraries( + deps: &[BuildDependency], + maven_repo: Option<&Path>, + gradle_home: Option<&Path>, +) -> Vec { + let mut deps: Vec<&BuildDependency> = deps.iter().collect(); + deps.sort(); + deps.dedup(); + deps.into_iter() + .filter_map(|d| { + let maven = || maven_repo.and_then(|r| maven_jar(r, d)); + let gradle = || gradle_home.and_then(|h| gradle_jar(h, d)); + let jar = if d.gradle { gradle().or_else(maven) } else { maven().or_else(gradle) }?; + let tool = if d.gradle { "Gradle" } else { "Maven" }; + let name = jar.file_name()?.to_string_lossy().into_owned(); + group( + format!("{tool}: {}:{}:{}", d.group, d.artifact, d.version), + &tool.to_lowercase(), + vec![LibraryRoot { name, path: ide_vfs::archive_root(&jar).into() }], + ) + }) + .collect() +} + +/// No path separators, interpolations or `..` segments (coordinates become paths). +fn valid_coordinate(d: &BuildDependency) -> bool { + [&d.group, &d.artifact, &d.version] + .iter() + .all(|p| !p.is_empty() && !p.contains(['/', '\\', '$']) && !p.split('.').any(str::is_empty)) +} + +/// `////-(-sources).jar`. +pub(crate) fn maven_jar(repo: &Path, d: &BuildDependency) -> Option { + if !valid_coordinate(d) { + return None; + } + let dir = repo.join(d.group.replace('.', "/")).join(&d.artifact).join(&d.version); + ["-sources.jar", ".jar"] + .iter() + .map(|suffix| dir.join(format!("{}-{}{suffix}", d.artifact, d.version))) + .find(|p| p.is_file()) +} + +/// `/caches/modules-2/files-2.1/////-(-sources).jar`. +pub(crate) fn gradle_jar(home: &Path, d: &BuildDependency) -> Option { + if !valid_coordinate(d) { + return None; + } + let dir = home.join("caches/modules-2/files-2.1").join(&d.group).join(&d.artifact).join(&d.version); + let mut hashes: Vec = std::fs::read_dir(dir).ok()?.flatten().map(|e| e.path()).collect(); + hashes.sort(); + ["-sources.jar", ".jar"].iter().find_map(|suffix| { + let file = format!("{}-{}{suffix}", d.artifact, d.version); + hashes.iter().map(|h| h.join(&file)).find(|p| p.is_file()) + }) +} + +// ------------------------------------------------------------------ node + +fn node_groups(root: &Path, s: &Settings) -> Vec { + let Some(files) = manifests(root, NODE_MANIFESTS) else { return Vec::new() }; + if !root.join("package.json").is_file() { + return Vec::new(); + } + cached(root, "node", s.languages.node.interpreter.clone(), &files, || { + let node = tool(crate::apply::node_dir(root, s), "node"); + node_libraries(root, node.as_deref()) + }) +} + +/// `node_modules/` of the direct dependencies in `package.json`, and the +/// Node.js core typings (`@types/node`, from the project or the runtime's global `lib`). +pub(crate) fn node_libraries(root: &Path, node: Option<&Path>) -> Vec { + let manifest = std::fs::read_to_string(root.join("package.json")).unwrap_or_default(); + let modules = root.join("node_modules"); + let deps = named_roots(package_dependencies(&manifest).into_iter().map(|d| (d.clone(), modules.join(d))).collect()); + let mut core = vec![modules.join("@types/node")]; + // `/bin/node` → `/lib/node_modules`. + if let Some(prefix) = + node.and_then(|n| dunce::canonicalize(n).ok()).and_then(|n| Some(n.parent()?.parent()?.to_path_buf())) + { + core.push(prefix.join("lib/node_modules/@types/node")); + } + let mut core = named_roots(core.into_iter().map(|p| ("@types/node".to_owned(), p)).collect()); + core.truncate(1); + [group("Node.js Core", "nodeCore", core), group("node_modules", "nodeModules", deps)] + .into_iter() + .flatten() + .collect() +} + +/// Names of the direct dependencies (all dependency kinds), sorted, deduplicated. +pub(crate) fn package_dependencies(package_json: &str) -> Vec { + let Ok(json) = serde_json::from_str::(package_json) else { return Vec::new() }; + let mut names: Vec = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"] + .iter() + .filter_map(|k| json.get(k)?.as_object()) + .flat_map(|o| o.keys().cloned()) + .filter(|n| !n.is_empty() && !n.contains("..") && !n.starts_with('/')) + .collect(); + names.sort(); + names.dedup(); + names +} + +// ------------------------------------------------------------------ rust + +fn rust_groups(root: &Path, s: &Settings) -> Vec { + let Some(files) = manifests(root, CARGO_MANIFESTS) else { return Vec::new() }; + if !root.join("Cargo.toml").is_file() { + return Vec::new(); + } + let r = &s.languages.rust; + cached(root, "rust", format!("{}\n{}", r.toolchain, r.stdlib), &files, || { + let bin = crate::apply::rust_bin(root, s).map(|(b, _)| b); + let mut out = Vec::new(); + if let Some(rustc) = tool(bin.clone(), "rustc") { + let version = query_in(&rustc, &["--version"], Some(root), &[], QUERY_TIMEOUT); + let library = crate::apply::setting_path(root, &r.stdlib).or_else(|| { + let sysroot = query_in(&rustc, &["--print", "sysroot"], Some(root), &[], QUERY_TIMEOUT)?; + Some(PathBuf::from(sysroot).join("lib/rustlib/src/rust/library")) + }); + let name = match version.as_deref().and_then(|v| v.split_whitespace().nth(1)) { + Some(v) => format!("Rust {v}"), + None => "Rust".into(), + }; + out.extend(library.and_then(|l| group(name, "rust", library_roots(vec![l])))); + } + if let Some(cargo) = tool(bin, "cargo") + && let Some(json) = + query_in(&cargo, &["metadata", "--format-version", "1", "--offline"], Some(root), &[], GRAPH_TIMEOUT) + { + out.extend(group("Cargo dependencies", "cargo", named_roots(parse_cargo_metadata(&json)))); + } + out + }) +} + +/// `(name version, source dir)` of the non-workspace packages in `cargo metadata` output, by name. +pub(crate) fn parse_cargo_metadata(json: &str) -> Vec<(String, PathBuf)> { + #[derive(Deserialize)] + struct Package { + name: String, + version: String, + source: Option, + manifest_path: PathBuf, + } + #[derive(Deserialize)] + struct Metadata { + packages: Vec, + } + let Ok(meta) = serde_json::from_str::(json) else { return Vec::new() }; + let mut out: Vec<(String, PathBuf)> = meta + .packages + .into_iter() + .filter(|p| p.source.is_some()) + .filter_map(|p| Some((format!("{} {}", p.name, p.version), p.manifest_path.parent()?.to_path_buf()))) + .collect(); + out.sort_by_cached_key(|(name, _)| name.to_lowercase()); + out +} + +// ------------------------------------------------------------------ go + +fn go_groups(root: &Path, s: &Settings) -> Vec { + let Some(files) = manifests(root, GO_MANIFESTS) else { return Vec::new() }; + if !root.join("go.mod").is_file() && !root.join("go.work").is_file() { + return Vec::new(); + } + let g = &s.languages.go; + cached(root, "go", format!("{}\n{}", g.goroot, g.gopath), &files, || { + let goroot = crate::apply::goroot(root, s); + let Some(go) = tool(goroot.as_ref().map(|r| r.join("bin")), "go") else { return Vec::new() }; + let goroot_s = goroot.as_ref().map(|p| p.to_string_lossy().into_owned()); + let gopath = crate::apply::setting_path(root, &g.gopath).map(|p| p.to_string_lossy().into_owned()); + // Never download modules or toolchains just to list libraries. + let mut env = vec![("GOPROXY", "off"), ("GOTOOLCHAIN", "local")]; + env.extend(goroot_s.as_deref().map(|v| ("GOROOT", v))); + env.extend(gopath.as_deref().map(|v| ("GOPATH", v))); + let mut out = Vec::new(); + if let Some(vars) = query_in(&go, &["env", "GOROOT", "GOVERSION"], Some(root), &env, QUERY_TIMEOUT) { + let mut lines = vars.lines().map(str::trim); + let (dir, version) = (lines.next().unwrap_or_default(), lines.next().unwrap_or_default()); + let name = match version.strip_prefix("go") { + Some(v) if !v.is_empty() => format!("Go SDK {v}"), + _ => "Go SDK".into(), + }; + if !dir.is_empty() { + out.extend(group(name, "go", library_roots(vec![Path::new(dir).join("src")]))); + } + } + if let Some(text) = query_in(&go, &["list", "-m", "-e", "-json", "all"], Some(root), &env, GRAPH_TIMEOUT) { + out.extend(group("Go Modules", "goModules", named_roots(parse_go_modules(&text)))); + } + out + }) +} + +/// `(path@version, dir)` of the downloaded non-main modules in `go list -m -json` output. +pub(crate) fn parse_go_modules(text: &str) -> Vec<(String, PathBuf)> { + #[derive(Deserialize)] + #[serde(rename_all = "PascalCase")] + struct Module { + path: String, + version: Option, + dir: Option, + #[serde(default)] + main: bool, + } + serde_json::Deserializer::from_str(text) + .into_iter::() + .map_while(Result::ok) + .filter(|m| !m.main) + .filter_map(|m| { + let name = match &m.version { + Some(v) => format!("{}@{v}", m.path), + None => m.path.clone(), + }; + Some((name, m.dir?)) + }) + .collect() +} diff --git a/crates/ide-sdk/src/tests.rs b/crates/ide-sdk/src/tests.rs index 0d74f038..86088a93 100644 --- a/crates/ide-sdk/src/tests.rs +++ b/crates/ide-sdk/src/tests.rs @@ -47,6 +47,7 @@ fn run(kind: &str, command: &str, args: &[&str]) -> RunConfig { is_test: false, debug_adapter: None, allow_parallel: false, + extras: Default::default(), } } @@ -290,3 +291,173 @@ fn custom_tool_path_replaces_the_command() { configure_server(&mut cfg, tmp.path(), tmp.path(), &s); assert_eq!(cfg.command, "zls"); } + +#[test] +fn python_library_paths_are_parsed_and_deduplicated() { + let tmp = tempfile::tempdir().unwrap(); + let stdlib = tmp.path().join("lib/python3.12"); + let site = stdlib.join("site-packages"); + std::fs::create_dir_all(&site).unwrap(); + let json = serde_json::to_string(&[Some(&stdlib), Some(&stdlib), Some(&site), None]).unwrap(); + let paths = crate::libraries::parse_python_paths(&format!("warning: something\n{json}")); + assert_eq!(paths.len(), 3); + let roots = crate::libraries::library_roots([paths, vec![tmp.path().join("gone")]].concat()); + assert_eq!(roots.iter().map(|r| r.name.as_str()).collect::>(), ["python3.12", "site-packages"]); + assert!(crate::libraries::parse_python_paths("not json").is_empty()); +} + +#[test] +fn no_libraries_without_an_interpreter() { + let tmp = tempfile::tempdir().unwrap(); + assert!(crate::libraries::project_libraries(tmp.path(), &Settings::default(), &[]).is_empty()); +} + +#[test] +fn node_libraries_list_direct_dependencies_and_core_typings() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + std::fs::write( + root.join("package.json"), + r#"{"dependencies":{"react":"^18","@scope/ui":"1"},"devDependencies":{"vitest":"2","react":"^18"}}"#, + ) + .unwrap(); + for dir in ["react", "@scope/ui", "@types/node", "transitive"] { + std::fs::create_dir_all(root.join("node_modules").join(dir)).unwrap(); + } + let groups = crate::libraries::node_libraries(root, None); + let summary: Vec<(&str, &str, Vec<&str>)> = groups + .iter() + .map(|g| (g.name.as_str(), g.kind.as_str(), g.roots.iter().map(|r| r.name.as_str()).collect())) + .collect(); + assert_eq!( + summary, + [ + ("Node.js Core", "nodeCore", vec!["@types/node"]), + ("node_modules", "nodeModules", vec!["@scope/ui", "react"]) + ] + ); + assert_eq!(crate::libraries::package_dependencies("{"), Vec::::new()); +} + +#[test] +fn cargo_metadata_lists_registry_packages_only() { + let tmp = tempfile::tempdir().unwrap(); + let serde = tmp.path().join("registry/src/index/serde-1.0.200"); + let json = serde_json::json!({ "packages": [ + { "name": "app", "version": "0.1.0", "source": null, "manifest_path": tmp.path().join("Cargo.toml") }, + { "name": "serde", "version": "1.0.200", "source": "registry+https://github.com/rust-lang/crates.io-index", + "manifest_path": serde.join("Cargo.toml") }, + ]}); + assert_eq!(crate::libraries::parse_cargo_metadata(&json.to_string()), [("serde 1.0.200".to_owned(), serde)]); + assert!(crate::libraries::parse_cargo_metadata("error: no Cargo.toml").is_empty()); +} + +#[test] +fn go_modules_skip_the_main_module_and_missing_downloads() { + let out = r#"{"Path":"example.com/app","Main":true,"Dir":"/p"} +{"Path":"golang.org/x/text","Version":"v0.14.0","Dir":"/gopath/pkg/mod/golang.org/x/text@v0.14.0"} +{"Path":"example.com/missing","Version":"v1.0.0","Error":{"Err":"module lookup disabled"}}"#; + assert_eq!( + crate::libraries::parse_go_modules(out), + [( + "golang.org/x/text@v0.14.0".to_owned(), + std::path::PathBuf::from("/gopath/pkg/mod/golang.org/x/text@v0.14.0") + )] + ); +} + +#[test] +fn jdk_library_is_the_jdk_home() { + let tmp = tempfile::tempdir().unwrap(); + let home = tmp.path().join("temurin-21.jdk/Contents/Home"); + std::fs::create_dir_all(&home).unwrap(); + assert_eq!(crate::libraries::jdk_name(&home).as_deref(), Some("temurin-21")); + assert_eq!(crate::libraries::jdk_name(&tmp.path().join("jdk-17")).as_deref(), Some("jdk-17")); + let group = crate::libraries::jdk_library(&crate::java::Jdk { major: 21, home: home.clone() }).unwrap(); + assert_eq!((group.name.as_str(), group.kind.as_str()), ("temurin-21", "jdk")); + assert_eq!(group.roots[0].path, ide_core::IpcPath::from(home.clone())); + assert_eq!(group.roots.len(), 1, "no sources archive"); + + std::fs::create_dir_all(home.join("lib")).unwrap(); + std::fs::write(home.join("lib/src.zip"), "").unwrap(); + let group = crate::libraries::jdk_library(&crate::java::Jdk { major: 21, home: home.clone() }).unwrap(); + assert_eq!(group.roots[1].name, "src.zip"); + assert_eq!(group.roots[1].path, ide_core::IpcPath::from(ide_vfs::archive_root(&home.join("lib/src.zip")))); +} + +#[test] +fn build_dependencies_resolve_to_local_jars() { + use crate::libraries::{BuildDependency, dependency_libraries}; + let tmp = tempfile::tempdir().unwrap(); + let (m2, gradle) = (tmp.path().join("m2"), tmp.path().join("gradle")); + let touch = |p: std::path::PathBuf| { + std::fs::create_dir_all(p.parent().unwrap()).unwrap(); + std::fs::write(&p, "").unwrap(); + p + }; + touch(m2.join("com/google/guava/guava/33.0/guava-33.0.jar")); + let guava_src = touch(m2.join("com/google/guava/guava/33.0/guava-33.0-sources.jar")); + let junit = touch(m2.join("junit/junit/4.13/junit-4.13.jar")); + let files = gradle.join("caches/modules-2/files-2.1/org.slf4j/slf4j-api/2.0.9"); + touch(files.join("a1b2/slf4j-api-2.0.9.jar")); + let slf4j_src = touch(files.join("c3d4/slf4j-api-2.0.9-sources.jar")); + let dep = |gradle: bool, g: &str, a: &str, v: &str| BuildDependency { + gradle, + group: g.into(), + artifact: a.into(), + version: v.into(), + }; + let deps = [ + dep(false, "junit", "junit", "4.13"), + dep(false, "com.google.guava", "guava", "33.0"), + dep(false, "com.google.guava", "guava", "33.0"), + dep(true, "org.slf4j", "slf4j-api", "2.0.9"), + dep(false, "missing", "nope", "1.0"), + dep(false, "..", "evil", "1"), + ]; + let groups = dependency_libraries(&deps, Some(&m2), Some(&gradle)); + let shown: Vec<(&str, &str, &str)> = + groups.iter().map(|g| (g.name.as_str(), g.kind.as_str(), g.roots[0].name.as_str())).collect(); + assert_eq!( + shown, + [ + ("Maven: com.google.guava:guava:33.0", "maven", "guava-33.0-sources.jar"), + ("Maven: junit:junit:4.13", "maven", "junit-4.13.jar"), + ("Gradle: org.slf4j:slf4j-api:2.0.9", "gradle", "slf4j-api-2.0.9-sources.jar"), + ] + ); + assert_eq!(groups[0].roots[0].path, ide_core::IpcPath::from(ide_vfs::archive_root(&guava_src))); + assert_eq!(groups[1].roots[0].path, ide_core::IpcPath::from(ide_vfs::archive_root(&junit))); + assert_eq!(groups[2].roots[0].path, ide_core::IpcPath::from(ide_vfs::archive_root(&slf4j_src))); + // A Gradle build also finds jars installed in the Maven local repository (`mavenLocal()`). + let groups = dependency_libraries(&[dep(true, "junit", "junit", "4.13")], Some(&m2), Some(&gradle)); + assert_eq!(groups[0].name, "Gradle: junit:junit:4.13"); +} + +#[test] +fn library_detection_is_cached_until_a_manifest_changes() { + let tmp = tempfile::tempdir().unwrap(); + let manifest = tmp.path().join("package.json"); + std::fs::write(&manifest, "{}").unwrap(); + let runs = std::cell::Cell::new(0); + let detect = || { + runs.set(runs.get() + 1); + Vec::new() + }; + let files = [manifest.clone()]; + crate::libraries::cached(tmp.path(), "test", "a".into(), &files, detect); + crate::libraries::cached(tmp.path(), "test", "a".into(), &files, detect); + assert_eq!(runs.get(), 1); + std::fs::write(&manifest, r#"{"dependencies":{}}"#).unwrap(); + crate::libraries::cached(tmp.path(), "test", "a".into(), &files, detect); + assert_eq!(runs.get(), 2); + crate::libraries::cached(tmp.path(), "test", "b".into(), &files, detect); + assert_eq!(runs.get(), 3); +} + +#[test] +fn no_ecosystem_libraries_without_manifests() { + let tmp = tempfile::tempdir().unwrap(); + std::fs::create_dir_all(tmp.path().join("node_modules/react")).unwrap(); + assert!(crate::libraries::project_libraries(tmp.path(), &Settings::default(), &[]).is_empty()); +} diff --git a/crates/ide-search/Cargo.toml b/crates/ide-search/Cargo.toml index fa4d17fe..fe41801c 100644 --- a/crates/ide-search/Cargo.toml +++ b/crates/ide-search/Cargo.toml @@ -7,6 +7,7 @@ license.workspace = true [dependencies] ide-core.workspace = true +ide-vfs.workspace = true serde.workspace = true specta.workspace = true grep-searcher.workspace = true diff --git a/crates/ide-search/src/engine.rs b/crates/ide-search/src/engine.rs index eeca9988..802595b0 100644 --- a/crates/ide-search/src/engine.rs +++ b/crates/ide-search/src/engine.rs @@ -3,6 +3,7 @@ use crate::iter::for_each_match; use crate::line::line_match; use crate::mask::FileMask; +use crate::pattern::ScopePattern; use crate::query::{Compiled, line_terminator}; use crate::{ FileMatches, LineMatch, LiteralKind, LiteralRange, Literals, Overlay, PathFilter, SearchContext, SearchScope, @@ -27,6 +28,7 @@ pub(crate) struct Request<'a> { pub scope: &'a SearchScope, pub compiled: Compiled, pub mask: FileMask, + pub pattern: Option, pub filter: Option>, pub context: SearchContext, pub literals: Option>, @@ -90,6 +92,7 @@ impl Control<'_> { struct Ctx<'a> { root: &'a Path, mask: &'a FileMask, + pattern: Option<&'a ScopePattern>, filter: Option>, context: SearchContext, literals: Option>, @@ -104,6 +107,7 @@ pub(crate) fn run(req: Request<'_>) -> IdeResult { let ctx = Ctx { root: req.root, mask: &req.mask, + pattern: req.pattern.as_ref(), filter: req.filter, context: req.context, literals: if req.context == SearchContext::Any { None } else { req.literals }, @@ -170,7 +174,8 @@ pub(crate) fn run(req: Request<'_>) -> IdeResult { return WalkState::Continue; } }; - if entry.file_type().is_some_and(|t| t.is_file()) { + // Archives (jar, zip) are browsed, not searched (IDEA skips them too). + if entry.file_type().is_some_and(|t| t.is_file()) && !ide_vfs::is_archive_file(entry.path()) { ctx.search_file(entry.path(), &matcher, &mut searcher); } if ctx.ctl.stopped() { WalkState::Quit } else { WalkState::Continue } @@ -199,7 +204,7 @@ pub(crate) fn roots(root: &Path, scope: &SearchScope) -> IdeResult> } }; Ok(match scope { - SearchScope::Project | SearchScope::FileSet { .. } => vec![dir(Path::new(""))?], + SearchScope::Project | SearchScope::FileSet { .. } | SearchScope::Pattern { .. } => vec![dir(Path::new(""))?], SearchScope::Directory { path } => vec![dir(path)?], SearchScope::Files { paths } => { let mut out: Vec = Vec::with_capacity(paths.len()); @@ -231,7 +236,10 @@ pub(crate) fn rel_path(root: &Path, path: &Path) -> String { impl Ctx<'_> { fn search_file(&self, path: &Path, matcher: &RegexMatcher, searcher: &mut Searcher) { let rel = rel_path(self.root, path); - if !self.mask.matches(&rel) || self.filter.is_some_and(|keep| !keep(&rel)) { + if !self.mask.matches(&rel) + || self.pattern.is_some_and(|p| !p.matches(&rel)) + || self.filter.is_some_and(|keep| !keep(&rel)) + { return; } // The search context needs the whole text to find comments and string literals. diff --git a/crates/ide-search/src/files.rs b/crates/ide-search/src/files.rs index f9f5b5e7..ba85acfc 100644 --- a/crates/ide-search/src/files.rs +++ b/crates/ide-search/src/files.rs @@ -27,7 +27,10 @@ pub fn scope_files( filter: Option>, cancel: &CancellationToken, ) -> IdeResult> { - let keep = |rel: &str| mask.matches(rel) && filter.is_none_or(|f| f(rel)); + let pattern = scope.pattern()?; + let keep = |rel: &str| { + mask.matches(rel) && pattern.as_ref().is_none_or(|p| p.matches(rel)) && filter.is_none_or(|f| f(rel)) + }; let found = Mutex::new(Vec::new()); let mut on_disk = Vec::new(); for path in roots(root, scope)? { @@ -63,7 +66,7 @@ pub fn scope_files( return WalkState::Quit; } let Ok(entry) = entry else { return WalkState::Continue }; - if entry.file_type().is_some_and(|t| t.is_file()) { + if entry.file_type().is_some_and(|t| t.is_file()) && !ide_vfs::is_archive_file(entry.path()) { let rel = rel_path(root, entry.path()); if keep(&rel) { found @@ -139,4 +142,15 @@ mod tests { vec!["src/a.rs", "src/new.rs"] ); } + + #[test] + fn pattern_scope() { + let dir = tree(); + let none = FileMask::new(&[], &[]).unwrap(); + let cancel = CancellationToken::new(); + let scope = SearchScope::Pattern { pattern: "file:src//*.rs&&!file:src/sub//*".into() }; + assert_eq!(rels(&scope_files(dir.path(), &scope, &none, None, &cancel).unwrap()), vec!["src/a.rs"]); + let bad = SearchScope::Pattern { pattern: "(".into() }; + assert!(scope_files(dir.path(), &bad, &none, None, &cancel).is_err()); + } } diff --git a/crates/ide-search/src/lib.rs b/crates/ide-search/src/lib.rs index 0e8b8fcf..8147272f 100644 --- a/crates/ide-search/src/lib.rs +++ b/crates/ide-search/src/lib.rs @@ -11,11 +11,13 @@ mod files; mod iter; mod line; mod mask; +mod pattern; mod query; mod replace; pub use files::{ScopeFile, scope_files}; pub use mask::FileMask; +pub use pattern::ScopePattern; pub use replace::{ ReplaceEvent, ReplaceHost, ReplacePreview, ReplaceSummary, ReplaceTarget, apply_edits, preserve_case, preview, replace_files, replacement_edits, @@ -114,6 +116,21 @@ pub enum SearchScope { FileSet { set: ProjectFileSet, }, + /// A custom scope: the project's files matching a [`ScopePattern`] + /// (`file:src//*.ts&&!file:*test*`). + Pattern { + pattern: String, + }, +} + +impl SearchScope { + /// The file pattern of a [`SearchScope::Pattern`]. + pub fn pattern(&self) -> IdeResult> { + match self { + Self::Pattern { pattern } => ScopePattern::parse(pattern).map(Some), + _ => Ok(None), + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -211,6 +228,7 @@ pub fn search_in_context( scope: &query.scope, compiled: query::compile(query)?, mask, + pattern: query.scope.pattern()?, filter, context: query.context, literals, @@ -225,6 +243,7 @@ pub fn search_in_context( /// invalid regex while the user types. pub fn validate_query(query: &SearchQuery) -> IdeResult<()> { FileMask::new(&query.include, &query.exclude)?; + query.scope.pattern()?; if !query.pattern.is_empty() { query::compile(query)?; } diff --git a/crates/ide-search/src/pattern.rs b/crates/ide-search/src/pattern.rs new file mode 100644 index 00000000..335aeaf6 --- /dev/null +++ b/crates/ide-search/src/pattern.rs @@ -0,0 +1,207 @@ +//! IDEA-style scope patterns for custom (named) scopes: +//! `file:src//*.ts&&!file:*test*`, `file:*.rs||file:docs//*`, `!(file:gen//*)`. +//! +//! A term is `file:` (the `file:` prefix and a `[module]` qualifier are +//! optional); `//` means "any depth" (`src//*` = `src/**/*`), a trailing `/` +//! means everything below. Globs without a `/` match the file name, others the +//! project-relative path, like [`crate::FileMask`]. Terms combine with `!`, +//! `&&`, `||` (in that precedence) and parentheses. + +use globset::{GlobBuilder, GlobMatcher}; +use ide_core::{IdeError, IdeResult}; + +#[derive(Debug)] +pub struct ScopePattern(Node); + +#[derive(Debug)] +enum Node { + Glob { glob: GlobMatcher, by_name: bool }, + Not(Box), + And(Vec), + Or(Vec), +} + +impl ScopePattern { + pub fn parse(source: &str) -> IdeResult { + let mut p = Parser { s: source, pos: 0 }; + p.skip_ws(); + if p.at_end() { + return Err(IdeError::invalid("Empty scope pattern")); + } + let node = p.or()?; + p.skip_ws(); + if !p.at_end() { + return Err(p.error("Unexpected input")); + } + Ok(Self(node)) + } + + /// `rel_path` is project-relative and `/`-separated. + pub fn matches(&self, rel_path: &str) -> bool { + self.0.matches(rel_path, rel_path.rsplit('/').next().unwrap_or(rel_path)) + } +} + +impl Node { + fn matches(&self, rel: &str, name: &str) -> bool { + match self { + Self::Glob { glob, by_name } => glob.is_match(if *by_name { name } else { rel }), + Self::Not(n) => !n.matches(rel, name), + Self::And(all) => all.iter().all(|n| n.matches(rel, name)), + Self::Or(any) => any.iter().any(|n| n.matches(rel, name)), + } + } +} + +struct Parser<'a> { + s: &'a str, + pos: usize, +} + +impl Parser<'_> { + fn rest(&self) -> &str { + &self.s[self.pos..] + } + + fn at_end(&self) -> bool { + self.pos >= self.s.len() + } + + fn skip_ws(&mut self) { + let rest = self.rest(); + self.pos += rest.len() - rest.trim_start().len(); + } + + fn eat(&mut self, token: &str) -> bool { + self.skip_ws(); + if self.rest().starts_with(token) { + self.pos += token.len(); + true + } else { + false + } + } + + fn error(&self, what: &str) -> IdeError { + IdeError::invalid(format!("{what} at column {} of the scope pattern", self.s[..self.pos].chars().count() + 1)) + } + + fn or(&mut self) -> IdeResult { + let mut items = vec![self.and()?]; + while self.eat("||") { + items.push(self.and()?); + } + Ok(if items.len() == 1 { items.remove(0) } else { Node::Or(items) }) + } + + fn and(&mut self) -> IdeResult { + let mut items = vec![self.unary()?]; + while self.eat("&&") { + items.push(self.unary()?); + } + Ok(if items.len() == 1 { items.remove(0) } else { Node::And(items) }) + } + + fn unary(&mut self) -> IdeResult { + if self.eat("!") { + return Ok(Node::Not(Box::new(self.unary()?))); + } + if self.eat("(") { + let node = self.or()?; + if !self.eat(")") { + return Err(self.error("Missing `)`")); + } + return Ok(node); + } + self.term() + } + + fn term(&mut self) -> IdeResult { + self.skip_ws(); + let rest = self.rest(); + let end = ["&&", "||", ")"].iter().filter_map(|t| rest.find(t)).min().unwrap_or(rest.len()); + let raw = rest[..end].trim(); + if raw.is_empty() { + return Err(self.error("Expected a file pattern")); + } + let glob = strip_prefix(raw); + if glob.is_empty() { + return Err(self.error("Expected a file pattern")); + } + let node = glob_node(glob)?; + self.pos += end; + Ok(node) + } +} + +/// `file:x`, `file[module]:x` → `x`. +fn strip_prefix(term: &str) -> &str { + let Some(rest) = term.strip_prefix("file") else { return term }; + let rest = match rest.strip_prefix('[') { + Some(r) => match r.find(']') { + Some(i) => &r[i + 1..], + None => return term, + }, + None => rest, + }; + rest.strip_prefix(':').map_or(term, str::trim) +} + +fn glob_node(pattern: &str) -> IdeResult { + let mut glob = pattern.trim_start_matches("./").replace("//", "/**/"); + if glob.ends_with('/') { + glob.push_str("**"); + } + let by_name = !glob.contains('/'); + let glob = if by_name { glob } else { glob.trim_start_matches('/').to_owned() }; + let matcher = GlobBuilder::new(&glob) + .literal_separator(true) + .backslash_escape(true) + .case_insensitive(cfg!(any(target_os = "macos", target_os = "windows"))) + .build() + .map_err(|e| IdeError::invalid(format!("Invalid file pattern `{pattern}`: {}", e.kind())))? + .compile_matcher(); + Ok(Node::Glob { glob: matcher, by_name }) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn p(s: &str) -> ScopePattern { + ScopePattern::parse(s).unwrap() + } + + #[test] + fn idea_recursive_directory_globs() { + let s = p("file:src//*.ts"); + assert!(s.matches("src/a.ts")); + assert!(s.matches("src/x/y/a.ts")); + assert!(!s.matches("lib/a.ts")); + assert!(!s.matches("src/a.rs")); + assert!(p("src/").matches("src/deep/a.rs")); + assert!(!p("file:src/*").matches("src/deep/a.rs")); + } + + #[test] + fn names_operators_and_grouping() { + let s = p("file:src//*&&!file:*test*"); + assert!(s.matches("src/main.rs")); + assert!(!s.matches("src/main_test.rs")); + let s = p("*.md || file[app]:docs//*"); + assert!(s.matches("README.md")); + assert!(s.matches("docs/x/y.txt")); + assert!(!s.matches("src/a.rs")); + let s = p("!(file:gen//* || *.lock) && *.*"); + assert!(s.matches("src/a.rs")); + assert!(!s.matches("gen/a.rs")); + assert!(!s.matches("Cargo.lock")); + } + + #[test] + fn errors() { + for bad in ["", " ", "file:", "a &&", "(a", "a)", "file:a["] { + assert!(ScopePattern::parse(bad).is_err(), "{bad:?}"); + } + } +} diff --git a/crates/ide-search/tests/search.rs b/crates/ide-search/tests/search.rs index c37cab14..412fe9b1 100644 --- a/crates/ide-search/tests/search.rs +++ b/crates/ide-search/tests/search.rs @@ -410,6 +410,20 @@ fn directory_scope() { assert_eq!(err.kind, ErrorKind::NotFound); } +#[test] +fn pattern_scope_intersects_the_file_mask() { + let p = mask_project(); + let scope = SearchScope::Pattern { pattern: "file:src//*&&!file:*Test*".into() }; + let q = SearchQuery { scope: scope.clone(), ..query("hit") }; + let (views, _) = run(p.path(), &q); + assert_eq!(paths(&views), ["src/Main.kt", "src/Util.java", "src/web/app.ts"]); + let q = SearchQuery { scope, include: vec!["*.ts".into()], ..query("hit") }; + let (views, _) = run(p.path(), &q); + assert_eq!(paths(&views), ["src/web/app.ts"]); + let bad = SearchQuery { scope: SearchScope::Pattern { pattern: "a &&".into() }, ..query("hit") }; + assert_eq!(ide_search::validate_query(&bad).unwrap_err().kind, ErrorKind::InvalidInput); +} + #[test] fn files_scope_searches_ignored_and_unsaved_files() { let p = project(&[(".gitignore", b"*.log\n"), ("a.log", b"hit\n"), ("b.txt", b"hit\n"), ("c.txt", b"hit\n")]); @@ -621,3 +635,11 @@ fn search_context_filters_comments_and_strings() { vec![s("a.rs", 0, &[(0, 3)]), s("a.rs", 1, &[(0, 3)]), s("b.txt", 0, &[(0, 3), (7, 10)])] ); } + +#[test] +fn archives_are_skipped() { + // A stored (uncompressed) zip entry would otherwise match before the first NUL byte. + let p = project(&[("lib/needle.jar", b"needle in a jar"), ("docs.zip", b"needle"), ("src/a.txt", b"needle\n")]); + let (views, _) = run(p.path(), &query("needle")); + assert_eq!(paths(&views), ["src/a.txt"]); +} diff --git a/crates/ide-spell/Cargo.toml b/crates/ide-spell/Cargo.toml new file mode 100644 index 00000000..11949309 --- /dev/null +++ b/crates/ide-spell/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ide-spell" +description = "Spell checking (Proofreading › Typo): word extraction from comments, strings and declared names, Hunspell en_US dictionary, user dictionaries" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +ide-core.workspace = true +parking_lot.workspace = true +tracing.workspace = true +# Pure-Rust Hunspell-compatible checker (MPL-2.0). +spellbook = "0.4" +serde.workspace = true +specta.workspace = true diff --git a/crates/ide-spell/src/dict/LICENSE-en_US b/crates/ide-spell/src/dict/LICENSE-en_US new file mode 100644 index 00000000..6b5b4908 --- /dev/null +++ b/crates/ide-spell/src/dict/LICENSE-en_US @@ -0,0 +1,347 @@ +en_US Hunspell Dictionary +Version 2020.12.07 +Mon Dec 7 20:14:35 2020 -0500 [5ef55f9] +http://wordlist.sourceforge.net + +README file for English Hunspell dictionaries derived from SCOWL. + +These dictionaries are created using the speller/make-hunspell-dict +script in SCOWL. + +The following dictionaries are available: + + en_US (American) + en_CA (Canadian) + en_GB-ise (British with "ise" spelling) + en_GB-ize (British with "ize" spelling) + en_AU (Australian) + + en_US-large + en_CA-large + en_GB-large (with both "ise" and "ize" spelling) + en_AU-large + +The normal (non-large) dictionaries correspond to SCOWL size 60 and, +to encourage consistent spelling, generally only include one spelling +variant for a word. The large dictionaries correspond to SCOWL size +70 and may include multiple spelling for a word when both variants are +considered almost equal. The larger dictionaries however (1) have not +been as carefully checked for errors as the normal dictionaries and +thus may contain misspelled or invalid words; and (2) contain +uncommon, yet valid, words that might cause problems as they are +likely to be misspellings of more common words (for example, "ort" and +"calender"). + +To get an idea of the difference in size, here are 25 random words +only found in the large dictionary for American English: + + Bermejo Freyr's Guenevere Hatshepsut Nottinghamshire arrestment + crassitudes crural dogwatches errorless fetial flaxseeds godroon + incretion jalapeño's kelpie kishkes neuroglias pietisms pullulation + stemwinder stenoses syce thalassic zees + +The en_US, en_CA and en_AU are the official dictionaries for Hunspell. +The en_GB and large dictionaries are made available on an experimental +basis. If you find them useful please send me a quick email at +kevina@gnu.org. + +If none of these dictionaries suite you (for example, maybe you want +the normal dictionary that also includes common variants) additional +dictionaries can be generated at http://app.aspell.net/create or by +modifying speller/make-hunspell-dict in SCOWL. Please do let me know +if you end up publishing a customized dictionary. + +If a word is not found in the dictionary or a word is there you think +shouldn't be, you can lookup the word up at http://app.aspell.net/lookup +to help determine why that is. + +General comments on these list can be sent directly to me at +kevina@gnu.org or to the wordlist-devel mailing lists +(https://lists.sourceforge.net/lists/listinfo/wordlist-devel). If you +have specific issues with any of these dictionaries please file a bug +report at https://github.com/kevina/wordlist/issues. + +IMPORTANT CHANGES INTRODUCED In 2016.11.20: + +New Australian dictionaries thanks to the work of Benjamin Titze +(btitze@protonmail.ch). + +IMPORTANT CHANGES INTRODUCED IN 2016.04.24: + +The dictionaries are now in UTF-8 format instead of ISO-8859-1. This +was required to handle smart quotes correctly. + +IMPORTANT CHANGES INTRODUCED IN 2016.01.19: + +"SET UTF8" was changes to "SET UTF-8" in the affix file as some +versions of Hunspell do not recognize "UTF8". + +ADDITIONAL NOTES: + +The NOSUGGEST flag was added to certain taboo words. While I made an +honest attempt to flag the strongest taboo words with the NOSUGGEST +flag, I MAKE NO GUARANTEE THAT I FLAGGED EVERY POSSIBLE TABOO WORD. +The list was originally derived from Németh László, however I removed +some words which, while being considered taboo by some dictionaries, +are not really considered swear words in today's society. + +COPYRIGHT, SOURCES, and CREDITS: + +The English dictionaries come directly from SCOWL +and is thus under the same copyright of SCOWL. The affix file is +a heavily modified version of the original english.aff file which was +released as part of Geoff Kuenning's Ispell and as such is covered by +his BSD license. Part of SCOWL is also based on Ispell thus the +Ispell copyright is included with the SCOWL copyright. + +The collective work is Copyright 2000-2018 by Kevin Atkinson as well +as any of the copyrights mentioned below: + + Copyright 2000-2018 by Kevin Atkinson + + Permission to use, copy, modify, distribute and sell these word + lists, the associated scripts, the output created from the scripts, + and its documentation for any purpose is hereby granted without fee, + provided that the above copyright notice appears in all copies and + that both that copyright notice and this permission notice appear in + supporting documentation. Kevin Atkinson makes no representations + about the suitability of this array for any purpose. It is provided + "as is" without express or implied warranty. + +Alan Beale also deserves special credit as he has, +in addition to providing the 12Dicts package and being a major +contributor to the ENABLE word list, given me an incredible amount of +feedback and created a number of special lists (those found in the +Supplement) in order to help improve the overall quality of SCOWL. + +The 10 level includes the 1000 most common English words (according to +the Moby (TM) Words II [MWords] package), a subset of the 1000 most +common words on the Internet (again, according to Moby Words II), and +frequently class 16 from Brian Kelk's "UK English Wordlist +with Frequency Classification". + +The MWords package was explicitly placed in the public domain: + + The Moby lexicon project is complete and has + been place into the public domain. Use, sell, + rework, excerpt and use in any way on any platform. + + Placing this material on internal or public servers is + also encouraged. The compiler is not aware of any + export restrictions so freely distribute world-wide. + + You can verify the public domain status by contacting + + Grady Ward + 3449 Martha Ct. + Arcata, CA 95521-4884 + + grady@netcom.com + grady@northcoast.com + +The "UK English Wordlist With Frequency Classification" is also in the +Public Domain: + + Date: Sat, 08 Jul 2000 20:27:21 +0100 + From: Brian Kelk + + > I was wondering what the copyright status of your "UK English + > Wordlist With Frequency Classification" word list as it seems to + > be lacking any copyright notice. + + There were many many sources in total, but any text marked + "copyright" was avoided. Locally-written documentation was one + source. An earlier version of the list resided in a filespace called + PUBLIC on the University mainframe, because it was considered public + domain. + + Date: Tue, 11 Jul 2000 19:31:34 +0100 + + > So are you saying your word list is also in the public domain? + + That is the intention. + +The 20 level includes frequency classes 7-15 from Brian's word list. + +The 35 level includes frequency classes 2-6 and words appearing in at +least 11 of 12 dictionaries as indicated in the 12Dicts package. All +words from the 12Dicts package have had likely inflections added via +my inflection database. + +The 12Dicts package and Supplement is in the Public Domain. + +The WordNet database, which was used in the creation of the +Inflections database, is under the following copyright: + + This software and database is being provided to you, the LICENSEE, + by Princeton University under the following license. By obtaining, + using and/or copying this software and database, you agree that you + have read, understood, and will comply with these terms and + conditions.: + + Permission to use, copy, modify and distribute this software and + database and its documentation for any purpose and without fee or + royalty is hereby granted, provided that you agree to comply with + the following copyright notice and statements, including the + disclaimer, and that the same appear on ALL copies of the software, + database and documentation, including modifications that you make + for internal use or for distribution. + + WordNet 1.6 Copyright 1997 by Princeton University. All rights + reserved. + + THIS SOFTWARE AND DATABASE IS PROVIDED "AS IS" AND PRINCETON + UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PRINCETON + UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- + ABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE + LICENSED SOFTWARE, DATABASE OR DOCUMENTATION WILL NOT INFRINGE ANY + THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + + The name of Princeton University or Princeton may not be used in + advertising or publicity pertaining to distribution of the software + and/or database. Title to copyright in this software, database and + any associated documentation shall at all times remain with + Princeton University and LICENSEE agrees to preserve same. + +The 40 level includes words from Alan's 3esl list found in version 4.0 +of his 12dicts package. Like his other stuff the 3esl list is also in the +public domain. + +The 50 level includes Brian's frequency class 1, words appearing +in at least 5 of 12 of the dictionaries as indicated in the 12Dicts +package, and uppercase words in at least 4 of the previous 12 +dictionaries. A decent number of proper names is also included: The +top 1000 male, female, and Last names from the 1990 Census report; a +list of names sent to me by Alan Beale; and a few names that I added +myself. Finally a small list of abbreviations not commonly found in +other word lists is included. + +The name files form the Census report is a government document which I +don't think can be copyrighted. + +The file special-jargon.50 uses common.lst and word.lst from the +"Unofficial Jargon File Word Lists" which is derived from "The Jargon +File". All of which is in the Public Domain. This file also contain +a few extra UNIX terms which are found in the file "unix-terms" in the +special/ directory. + +The 55 level includes words from Alan's 2of4brif list found in version +4.0 of his 12dicts package. Like his other stuff the 2of4brif is also +in the public domain. + +The 60 level includes all words appearing in at least 2 of the 12 +dictionaries as indicated by the 12Dicts package. + +The 70 level includes Brian's frequency class 0 and the 74,550 common +dictionary words from the MWords package. The common dictionary words, +like those from the 12Dicts package, have had all likely inflections +added. The 70 level also included the 5desk list from version 4.0 of +the 12Dics package which is in the public domain. + +The 80 level includes the ENABLE word list, all the lists in the +ENABLE supplement package (except for ABLE), the "UK Advanced Cryptics +Dictionary" (UKACD), the list of signature words from the YAWL package, +and the 10,196 places list from the MWords package. + +The ENABLE package, mainted by M\Cooper , +is in the Public Domain: + + The ENABLE master word list, WORD.LST, is herewith formally released + into the Public Domain. Anyone is free to use it or distribute it in + any manner they see fit. No fee or registration is required for its + use nor are "contributions" solicited (if you feel you absolutely + must contribute something for your own peace of mind, the authors of + the ENABLE list ask that you make a donation on their behalf to your + favorite charity). This word list is our gift to the Scrabble + community, as an alternate to "official" word lists. Game designers + may feel free to incorporate the WORD.LST into their games. Please + mention the source and credit us as originators of the list. Note + that if you, as a game designer, use the WORD.LST in your product, + you may still copyright and protect your product, but you may *not* + legally copyright or in any way restrict redistribution of the + WORD.LST portion of your product. This *may* under law restrict your + rights to restrict your users' rights, but that is only fair. + +UKACD, by J Ross Beresford , is under the +following copyright: + + Copyright (c) J Ross Beresford 1993-1999. All Rights Reserved. + + The following restriction is placed on the use of this publication: + if The UK Advanced Cryptics Dictionary is used in a software package + or redistributed in any form, the copyright notice must be + prominently displayed and the text of this document must be included + verbatim. + + There are no other restrictions: I would like to see the list + distributed as widely as possible. + +The 95 level includes the 354,984 single words, 256,772 compound +words, 4,946 female names and the 3,897 male names, and 21,986 names +from the MWords package, ABLE.LST from the ENABLE Supplement, and some +additional words found in my part-of-speech database that were not +found anywhere else. + +Accent information was taken from UKACD. + +The VarCon package was used to create the American, British, Canadian, +and Australian word list. It is under the following copyright: + + Copyright 2000-2016 by Kevin Atkinson + + Permission to use, copy, modify, distribute and sell this array, the + associated software, and its documentation for any purpose is hereby + granted without fee, provided that the above copyright notice appears + in all copies and that both that copyright notice and this permission + notice appear in supporting documentation. Kevin Atkinson makes no + representations about the suitability of this array for any + purpose. It is provided "as is" without express or implied warranty. + + Copyright 2016 by Benjamin Titze + + Permission to use, copy, modify, distribute and sell this array, the + associated software, and its documentation for any purpose is hereby + granted without fee, provided that the above copyright notice appears + in all copies and that both that copyright notice and this permission + notice appear in supporting documentation. Benjamin Titze makes no + representations about the suitability of this array for any + purpose. It is provided "as is" without express or implied warranty. + + Since the original words lists come from the Ispell distribution: + + Copyright 1993, Geoff Kuenning, Granada Hills, CA + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All modifications to the source code must be clearly marked as + such. Binary redistributions based on modified source code + must be clearly marked as modified versions in the documentation + and/or other materials provided with the distribution. + (clause 4 removed with permission from Geoff Kuenning) + 5. The name of Geoff Kuenning may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +Build Date: Mon Dec 7 20:19:27 EST 2020 +Wordlist Command: mk-list --accents=strip en_US 60 diff --git a/crates/ide-spell/src/dict/en_US.aff b/crates/ide-spell/src/dict/en_US.aff new file mode 100644 index 00000000..9e7cb1e2 --- /dev/null +++ b/crates/ide-spell/src/dict/en_US.aff @@ -0,0 +1,205 @@ +SET UTF-8 +TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ' +ICONV 1 +ICONV ’ ' +NOSUGGEST ! + +# ordinal numbers +COMPOUNDMIN 1 +# only in compounds: 1th, 2th, 3th +ONLYINCOMPOUND c +# compound rules: +# 1. [0-9]*1[0-9]th (10th, 11th, 12th, 56714th, etc.) +# 2. [0-9]*[02-9](1st|2nd|3rd|[4-9]th) (21st, 22nd, 123rd, 1234th, etc.) +COMPOUNDRULE 2 +COMPOUNDRULE n*1t +COMPOUNDRULE n*mp +WORDCHARS 0123456789 + +PFX A Y 1 +PFX A 0 re . + +PFX I Y 1 +PFX I 0 in . + +PFX U Y 1 +PFX U 0 un . + +PFX C Y 1 +PFX C 0 de . + +PFX E Y 1 +PFX E 0 dis . + +PFX F Y 1 +PFX F 0 con . + +PFX K Y 1 +PFX K 0 pro . + +SFX V N 2 +SFX V e ive e +SFX V 0 ive [^e] + +SFX N Y 3 +SFX N e ion e +SFX N y ication y +SFX N 0 en [^ey] + +SFX X Y 3 +SFX X e ions e +SFX X y ications y +SFX X 0 ens [^ey] + +SFX H N 2 +SFX H y ieth y +SFX H 0 th [^y] + +SFX Y Y 1 +SFX Y 0 ly . + +SFX G Y 2 +SFX G e ing e +SFX G 0 ing [^e] + +SFX J Y 2 +SFX J e ings e +SFX J 0 ings [^e] + +SFX D Y 4 +SFX D 0 d e +SFX D y ied [^aeiou]y +SFX D 0 ed [^ey] +SFX D 0 ed [aeiou]y + +SFX T N 4 +SFX T 0 st e +SFX T y iest [^aeiou]y +SFX T 0 est [aeiou]y +SFX T 0 est [^ey] + +SFX R Y 4 +SFX R 0 r e +SFX R y ier [^aeiou]y +SFX R 0 er [aeiou]y +SFX R 0 er [^ey] + +SFX Z Y 4 +SFX Z 0 rs e +SFX Z y iers [^aeiou]y +SFX Z 0 ers [aeiou]y +SFX Z 0 ers [^ey] + +SFX S Y 4 +SFX S y ies [^aeiou]y +SFX S 0 s [aeiou]y +SFX S 0 es [sxzh] +SFX S 0 s [^sxzhy] + +SFX P Y 3 +SFX P y iness [^aeiou]y +SFX P 0 ness [aeiou]y +SFX P 0 ness [^y] + +SFX M Y 1 +SFX M 0 's . + +SFX B Y 3 +SFX B 0 able [^aeiou] +SFX B 0 able ee +SFX B e able [^aeiou]e + +SFX L Y 1 +SFX L 0 ment . + +REP 90 +REP a ei +REP ei a +REP a ey +REP ey a +REP ai ie +REP ie ai +REP alot a_lot +REP are air +REP are ear +REP are eir +REP air are +REP air ere +REP ere air +REP ere ear +REP ere eir +REP ear are +REP ear air +REP ear ere +REP eir are +REP eir ere +REP ch te +REP te ch +REP ch ti +REP ti ch +REP ch tu +REP tu ch +REP ch s +REP s ch +REP ch k +REP k ch +REP f ph +REP ph f +REP gh f +REP f gh +REP i igh +REP igh i +REP i uy +REP uy i +REP i ee +REP ee i +REP j di +REP di j +REP j gg +REP gg j +REP j ge +REP ge j +REP s ti +REP ti s +REP s ci +REP ci s +REP k cc +REP cc k +REP k qu +REP qu k +REP kw qu +REP o eau +REP eau o +REP o ew +REP ew o +REP oo ew +REP ew oo +REP ew ui +REP ui ew +REP oo ui +REP ui oo +REP ew u +REP u ew +REP oo u +REP u oo +REP u oe +REP oe u +REP u ieu +REP ieu u +REP ue ew +REP ew ue +REP uff ough +REP oo ieu +REP ieu oo +REP ier ear +REP ear ier +REP ear air +REP air ear +REP w qu +REP qu w +REP z ss +REP ss z +REP shun tion +REP shun sion +REP shun cion +REP size cise diff --git a/crates/ide-spell/src/dict/en_US.dic b/crates/ide-spell/src/dict/en_US.dic new file mode 100644 index 00000000..b41fd203 --- /dev/null +++ b/crates/ide-spell/src/dict/en_US.dic @@ -0,0 +1,49569 @@ +49568 +0/nm +0th/pt +1/n1 +1st/p +1th/tc +2/nm +2nd/p +2th/tc +3/nm +3rd/p +3th/tc +4/nm +4th/pt +5/nm +5th/pt +6/nm +6th/pt +7/nm +7th/pt +8/nm +8th/pt +9/nm +9th/pt +A/SM +AA/M +AAA +AB/M +ABA +ABC/SM +ABM/SM +ABS +AC/M +ACLU/M +ACT +ACTH/M +AD/M +ADC +ADD +ADM +ADP/M +AF +AFAIK +AFB +AFC/M +AFDC +AFN +AFT +AI/SM +AIDS/M +AK +AL +AM/M +AMA +AMD/M +ANSI/S +ANZUS/M +AOL/M +AP/M +APB +APC +API +APO +APR +AR +ARC +ASAP +ASCII/SM +ASL/M +ASPCA +ATM/M +ATP/M +ATV +AV +AVI +AWACS/M +AWOL/M +AWS/M +AZ/M +AZT/M +Aachen/M +Aaliyah/M +Aaron/M +Abbas/M +Abbasid/M +Abbott/M +Abby/M +Abdul/M +Abe/M +Abel/M +Abelard/M +Abelson/M +Aberdeen/M +Abernathy/M +Abidjan/M +Abigail/M +Abilene/M +Abner/M +Aborigine/MS +Abraham/M +Abram/MS +Abrams/M +Absalom/M +Abuja/M +Abyssinia/M +Abyssinian/M +Ac/M +Acadia/M +Acapulco/M +Accenture/M +Accra/M +Acevedo/M +Achaean/M +Achebe/M +Achernar/M +Acheson/M +Achilles/M +Aconcagua/M +Acosta/M +Acropolis +Acrux/M +Actaeon/M +Acton/M +Acts/M +Acuff/M +Ada/SM +Adam/SM +Adams/M +Adan/M +Adana/M +Adar/M +Addams/M +Adderley/M +Addie/M +Addison/M +Adela/M +Adelaide/M +Adele/M +Adeline/M +Aden/M +Adenauer/M +Adhara/M +Adidas/M +Adirondack/SM +Adirondacks/M +Adkins/M +Adler/M +Adm +Admiralty +Adolf/M +Adolfo/M +Adolph/M +Adonis/MS +Adrenalin/MS +Adrian/M +Adriana/M +Adriatic/M +Adrienne/M +Advent/MS +Adventist/MS +Advil/M +Aegean/M +Aelfric/M +Aeneas/M +Aeneid/M +Aeolus/M +Aeroflot/M +Aeschylus/M +Aesculapius/M +Aesop/M +Afghan/SM +Afghani/M +Afghanistan/M +Afr +Africa/M +African/SM +Afrikaans/M +Afrikaner/SM +Afro/SM +Afrocentric +Afrocentrism/M +Ag/M +Agamemnon/M +Agana +Agassi/M +Agassiz/M +Agatha/M +Aggie/M +Aglaia/M +Agnes/M +Agnew/M +Agni/M +Agra/M +Agricola/M +Agrippa/M +Agrippina/M +Aguadilla/M +Aguascalientes +Aguilar/M +Aguinaldo/M +Aguirre/M +Agustin/M +Ahab/M +Ahmad/M +Ahmadabad/M +Ahmadinejad/M +Ahmed/M +Ahriman/M +Aida/M +Aiken/M +Aileen/M +Aimee/M +Ainu/M +Airedale/MS +Aires/M +Aisha/M +Ajax/M +Akbar/M +Akhmatova/M +Akihito/M +Akita/M +Akiva/M +Akkad/M +Akron/M +Al/M +Ala/S +Alabama/M +Alabaman/MS +Alabamian/SM +Aladdin/M +Alamo/M +Alamogordo/M +Alan/M +Alana/M +Alar/M +Alaric/M +Alaska/M +Alaskan/MS +Alba/M +Albania/M +Albanian/MS +Albany/M +Albee/M +Alberio/M +Albert/M +Alberta/M +Albertan +Alberto/M +Albigensian/M +Albion/M +Albireo/M +Albuquerque/M +Alcatraz/M +Alcestis/M +Alcibiades/M +Alcindor/M +Alcmena/M +Alcoa/M +Alcott/M +Alcuin/M +Alcyone/M +Aldan/M +Aldebaran/M +Alden/M +Alderamin/M +Aldo/M +Aldrin/M +Alec/M +Aleichem/M +Alejandra/M +Alejandro/M +Alembert/M +Aleppo/M +Aleut/MS +Aleutian/SM +Alex/M +Alexander/MS +Alexandra/M +Alexandria/M +Alexandrian +Alexei/M +Alexis/M +Alfonso/M +Alfonzo/M +Alford/M +Alfred/M +Alfreda/M +Alfredo/M +Algenib/M +Alger/M +Algeria/M +Algerian/SM +Algieba/M +Algiers/M +Algol/M +Algonquian/SM +Algonquin/MS +Alhambra/M +Alhena/M +Ali/M +Alice/M +Alicia/M +Alighieri/M +Aline/M +Alioth/M +Alisa/M +Alisha/M +Alison/M +Alissa/M +Alistair/M +Alkaid/M +Allah/M +Allahabad/M +Allan/M +Alleghenies/M +Allegheny/SM +Allegra/M +Allen/M +Allende/M +Allentown/M +Allhallows/M +Allie/MS +Allison/M +Allstate/M +Allyson/M +Alma/M +Almach/M +Almaty/M +Almighty/M +Almohad/M +Almoravid/M +Alnilam/M +Alnitak/M +Alonzo/M +Alpert/M +Alphard/M +Alphecca/M +Alpheratz/M +Alphonse/M +Alphonso/M +Alpine/M +Alpo/M +Alps/M +Alsace/M +Alsatian/SM +Alsop/M +Alston/M +Alta/M +Altaba/M +Altai/M +Altaic/M +Altair/M +Altamira/M +Althea/M +Altiplano/M +Altman/M +Altoids/M +Alton/M +Altoona/M +Aludra/M +Alva/M +Alvarado/M +Alvarez/M +Alvaro/M +Alvin/M +Alyce/M +Alyson/M +Alyssa/M +Alzheimer/M +Am/MNR +Amadeus/M +Amado/M +Amalia/M +Amanda/M +Amarillo/M +Amaru/M +Amaterasu/M +Amati/M +Amazon/SM +Amazonian +Amber/M +Amelia/M +Amen/M +Amenhotep/M +Amerasian/M +America/SM +American/MS +Americana/M +Americanism/MS +Americanization/MS +Americanize/GDS +Amerind/SM +Amerindian/MS +Ames/M +Ameslan/M +Amgen/M +Amharic/M +Amherst/M +Amie/M +Amiga/M +Amish/M +Amman/M +Amoco/M +Amos/M +Amparo/M +Ampere/M +Amritsar/M +Amsterdam/M +Amtrak/M +Amundsen/M +Amur/M +Amway/M +Amy/M +Ana/M +Anabaptist/M +Anabel/M +Anacin/M +Anacreon/M +Anaheim/M +Analects/M +Ananias/M +Anasazi/M +Anastasia/M +Anatole/M +Anatolia/M +Anatolian/M +Anaxagoras/M +Anchorage/M +Andalusia/M +Andalusian/M +Andaman/M +Andean/M +Andersen/M +Anderson/M +Andes/M +Andorra/M +Andorran/SM +Andre/MS +Andrea/M +Andrei/M +Andres/M +Andretti/M +Andrew/SM +Andrews/M +Andrianampoinimerina/M +Android/M +Andromache/M +Andromeda/M +Andropov/M +Andy/M +Angara/M +Angel/M +Angela/M +Angeles/M +Angelia/M +Angelica/M +Angelico/M +Angelina/M +Angeline/M +Angelique/M +Angelita/M +Angelo/M +Angelou/M +Angevin/M +Angie/M +Angkor/M +Angle/MS +Angleton/M +Anglia/M +Anglican/SM +Anglicanism/MS +Anglicism/MS +Anglicization +Anglicize +Anglo/M +Anglophile/M +Anglophobe +Angola/M +Angolan/MS +Angora/SM +Angstrom/M +Anguilla/M +Angus/M +Anhui/M +Aniakchak/M +Anibal/M +Anita/M +Ankara/M +Ann/M +Anna/M +Annabel/M +Annabelle/M +Annam/M +Annapolis/M +Annapurna/M +Anne/M +Annette/M +Annie/M +Anniston/M +Annmarie/M +Annunciation/SM +Anouilh/M +Anselm/M +Anselmo/M +Anshan/M +Antaeus/M +Antananarivo/M +Antarctic/M +Antarctica/M +Antares/M +Anthony/M +Anthropocene +Antichrist/SM +Antietam/M +Antifa/M +Antigone/M +Antigua/M +Antillean +Antilles/M +Antioch/M +Antipas/M +Antipodes +Antofagasta/M +Antoine/M +Antoinette/M +Anton/M +Antone/M +Antonia/M +Antoninus/M +Antonio/M +Antonius/M +Antony/M +Antwan/M +Antwerp/M +Anubis/M +Anzac/M +Apache/SM +Apalachicola/M +Apatosaurus +Apennines/M +Aphrodite/M +Apia/M +Apocalypse/M +Apocrypha/M +Apollinaire/M +Apollo/SM +Apollonian/M +Apostle/M +Appalachia/M +Appalachian/SM +Appalachians/M +Appaloosa/SM +Apple/M +Appleseed/M +Appleton/M +Appomattox/M +Apr/M +April/MS +Apuleius/M +Aquafresh/M +Aquarian +Aquarius/MS +Aquila/M +Aquinas/M +Aquino/M +Aquitaine/M +Ar/M +Ara/M +Arab/SM +Arabia/M +Arabian/MS +Arabic/M +Arabist/MS +Araby/M +Araceli/M +Arafat/M +Aragon +Araguaya/M +Aral/M +Aramaic/M +Aramco/M +Arapaho/MS +Arapahoes +Ararat/M +Araucanian/M +Arawak/M +Arawakan/M +Arbitron/M +Arcadia/M +Arcadian/M +Archean/M +Archibald/M +Archie/M +Archimedes/M +Arctic/M +Arcturus/M +Ardabil +Arden/M +Arduino/M +Arecibo/M +Arequipa/M +Ares/M +Argentina/M +Argentine/M +Argentinean +Argentinian/MS +Argo/SM +Argonaut/MS +Argonne/M +Argos/M +Argus/M +Ariadne/M +Arianism/M +Ariel/M +Aries/MS +Ariosto/M +Aristarchus/M +Aristides/M +Aristophanes/M +Aristotelian/M +Aristotle/M +Arius/M +Ariz +Arizona/M +Arizonan/SM +Arizonian/MS +Arjuna/M +Ark/M +Arkansan/MS +Arkansas/M +Arkhangelsk/M +Arkwright/M +Arlene/M +Arline/M +Arlington/M +Armageddon/SM +Armagnac/M +Armand/M +Armando/M +Armani/M +Armenia/M +Armenian/SM +Arminius/M +Armonk/M +Armour/M +Armstrong/M +Arneb/M +Arnhem/M +Arno/M +Arnold/M +Arnulfo/M +Aron/M +Arrhenius/M +Arron/M +Art/M +Artaxerxes/M +Artemis/M +Arthur/M +Arthurian/M +Artie/M +Arturo/M +Aruba/M +Aryan/MS +As/M +Asama/M +Ascella/M +Ascension/M +Asgard/M +Ashanti/M +Ashcroft/M +Ashe/M +Asheville/M +Ashgabat +Ashikaga/M +Ashkenazim/M +Ashkhabad/M +Ashlee/M +Ashley/M +Ashmolean/M +Ashurbanipal/M +Asia/M +Asiago +Asian/MS +Asiatic/SM +Asimov/M +Asmara/M +Asoka/M +Aspell/M +Aspen/M +Asperger/M +Aspidiske/M +Asquith/M +Assad/M +Assam/M +Assamese/M +Assembly +Assisi/M +Assyria/M +Assyrian/SM +Astaire/M +Astana/M +Astarte/M +Aston/M +Astor/M +Astoria/M +Astrakhan/M +AstroTurf/M +Asturias/M +Asuncion/M +Aswan/M +At/SM +Atacama/M +Atahualpa/M +Atalanta/M +Atari/M +Atascadero/M +Ataturk/M +Athabasca/M +Athabaskan/SM +Athanasius +Athena/M +Athene/M +Athenian/SM +Athens/M +Atkins/M +Atkinson/M +Atlanta/M +Atlantes +Atlantic/M +Atlantis/M +Atlas/MS +Atman/M +Atonement +Atreus/M +Atria/M +Atropos/M +Attic/M +Attica/M +Attila/M +Attlee/M +Attn +Attucks/M +Atwood/M +Au/M +Aubrey/M +Auburn/M +Auckland/M +Auden/M +Audi/M +Audion/M +Audra/M +Audrey/M +Audubon/M +Aug/M +Augean/M +Augsburg/M +August/MS +Augusta/M +Augustan/M +Augustine/M +Augustinian/MS +Augustus/M +Aurangzeb/M +Aurelia/M +Aurelio/M +Aurelius/M +Aureomycin/M +Auriga/M +Aurora/M +Auschwitz/M +Aussie/MS +Austen/M +Austerlitz/M +Austin/MS +Australasia/M +Australasian +Australia/M +Australian/SM +Australoid/M +Australopithecus/M +Austria/M +Austrian/SM +Austronesian/M +Autumn/M +Av/M +Ava/M +Avalon/M +Ave/M +Aventine/M +Avernus/M +Averroes/M +Avery/M +Avesta/M +Avicenna/M +Avignon/M +Avila/M +Avior/M +Avis/M +Avogadro/M +Avon/M +Avondale/M +Axis +Axum/M +Ayala/M +Ayers/M +Aymara/M +Ayrshire/M +Ayurveda/M +Ayyubid/M +Azana/M +Azania/M +Azazel/M +Azerbaijan/M +Azerbaijani/MS +Azores/M +Azov/M +Aztec/SM +Aztecan/M +Aztlan/M +B/MNT +BA/M +BASIC/SM +BB/M +BBB/M +BBC/M +BBQ +BBS +BBSes +BC/M +BFF +BIA +BIOS +BITNET +BLT/SM +BM/M +BMW/M +BO +BP/M +BPOE +BR +BS/M +BSA +BSD/SM +BTU +BTW +BYOB +Ba/M +Baal/SM +Baath/M +Baathist/M +Babbage/M +Babbitt/M +Babel/MS +Babylon/MS +Babylonia/M +Babylonian/SM +Bacall/M +Bacardi/M +Bacchanalia/M +Bacchic +Bacchus/M +Bach/M +Backus/M +Bacon/M +Bactria/M +Baden/M +Badlands/M +Baedeker/MS +Baez/M +Baffin/M +Baggies/M +Baghdad/M +Baguio/M +Baha'i/M +Baha'ullah/M +Bahama/SM +Bahamanian +Bahamas/M +Bahamian/MS +Bahia/M +Bahrain/M +Baidu/M +Baikal/M +Bailey/M +Baird/M +Bakelite/M +Baker/M +Bakersfield/M +Baku/M +Bakunin/M +Balanchine/M +Balaton/M +Balboa/M +Balder/M +Baldwin/SM +Balearic/M +Balfour/M +Bali/M +Balinese/M +Balkan/MS +Balkans/M +Balkhash/M +Ball/M +Ballard/M +Balthazar/M +Baltic/M +Baltimore/M +Baluchistan/M +Balzac/M +Bamako/M +Bambi/M +Banach/M +Bancroft/M +Bandung/M +Bangalore/M +Bangkok/M +Bangladesh/M +Bangladeshi/SM +Bangor/M +Bangui/M +Banjarmasin/M +Banjul/M +Banks/M +Banneker/M +Bannister/M +Banting/M +Bantu/MS +Baotou/M +Baptist/SM +Baptiste/M +Barabbas/M +Barack/M +Barbadian/SM +Barbados/M +Barbara/M +Barbarella/M +Barbarossa/M +Barbary/M +Barber/M +Barbie/M +Barbour/M +Barbra/M +Barbuda/M +Barcelona/M +Barceloneta/M +Barclay/SM +Barclays/M +Bardeen/M +Barents/M +Barker/M +Barkley/M +Barlow/M +Barnabas/M +Barnaby/M +Barnard/M +Barnaul/M +Barnes/M +Barnett/M +Barney/M +Barnum/M +Baroda/M +Barquisimeto/M +Barr/M +Barranquilla/M +Barrera/M +Barrett/M +Barrie/M +Barron/M +Barry/M +Barrymore/M +Bart/M +Barth/MS +Bartholdi/M +Bartholomew/M +Bartlett/M +Bartok/M +Barton/M +Baruch/M +Baryshnikov/M +Basel/M +Basho/M +Basie/M +Basil/M +Basque/MS +Basra/M +Bass/M +Basseterre/M +Bastille/M +Basutoland/M +Bataan/M +Bates/M +Bathsheba/M +Batista/M +Batman/M +Battle/M +Batu/M +Baudelaire/M +Baudouin/M +Baudrillard/M +Bauer/M +Bauhaus/M +Baum/M +Bavaria/M +Bavarian/M +Baxter/M +Bayamon +Bayer/M +Bayes/M +Bayesian/M +Bayeux/M +Baylor/M +Bayonne/M +Bayreuth/M +Baywatch/M +Be/MH +Beach/M +Beadle/M +Bean/M +Beard/M +Beardmore/M +Beardsley/M +Bearnaise/M +Beasley/M +Beatlemania/M +Beatles/M +Beatrice/M +Beatrix/M +Beatriz/M +Beatty/M +Beau/M +Beaufort/M +Beaujolais/M +Beaumarchais/M +Beaumont/M +Beauregard/M +Beauvoir/M +Bechtel/M +Beck/MR +Becker/M +Becket/M +Beckett/M +Beckley/M +Beckman +Becky/M +Becquerel/M +Bede/M +Bedouin/SM +Beebe/M +Beecher/M +Beefaroni/M +Beelzebub/M +Beerbohm/M +Beethoven/M +Beeton/M +Begin/M +Behan/M +Behring/M +Beiderbecke/M +Beijing/M +Beirut/M +Bekesy/M +Bela/M +Belarus/M +Belarusian +Belau/M +Belem/M +Belfast/M +Belg +Belgian/SM +Belgium/M +Belgrade/M +Belinda/M +Belize/M +Bell/M +Bella/M +Bellamy/M +Bellatrix/M +Belleek/M +Bellingham/M +Bellini/M +Bellow/M +Belmont/M +Belmopan/M +Beloit/M +Belorussian/MS +Belshazzar/M +Beltane/M +Belushi/M +Ben/M +Benacerraf/M +Benchley/M +Bend/MR +Bender/M +Bendictus +Bendix/M +Benedict/M +Benedictine/MS +Benelux/M +Benet/M +Benetton/M +Bengal/SM +Bengali/M +Benghazi/M +Benin/M +Beninese/M +Benita/M +Benito/M +Benjamin/M +Bennett/M +Bennie/M +Benny/M +Benson/M +Bentham/M +Bentley/M +Benton/M +Benz/M +Benzedrine/M +Beowulf/M +Berber/SM +Berenice/M +Beretta/M +Berg/MNR +Bergen/M +Berger/M +Bergerac/M +Bergman/M +Bergson/M +Beria/M +Bering/M +Berkeley/M +Berkshire/SM +Berkshires/M +Berle/M +Berlin/SZMR +Berliner/M +Berlioz/M +Berlitz/M +Bermuda/SM +Bermudan/SM +Bermudian/SM +Bern/M +Bernadette/M +Bernadine/M +Bernanke/M +Bernard/M +Bernardo/M +Bernays/M +Bernbach/M +Bernese +Bernhardt/M +Bernice/M +Bernie/M +Bernini/M +Bernoulli/M +Bernstein/M +Berra/M +Berry/M +Bert/M +Berta/M +Bertelsmann/M +Bertha/M +Bertie/M +Bertillon/M +Bertram/M +Bertrand/M +Berwick/M +Beryl/M +Berzelius/M +Bess/M +Bessel/M +Bessemer/M +Bessie/M +Best/M +Betelgeuse/M +Beth/M +Bethany/M +Bethe/M +Bethesda/M +Bethlehem/M +Bethune/M +Betsy/M +Bette/M +Bettie/M +Betty/M +Bettye/M +Beulah/M +Beveridge +Beverley/M +Beverly/M +Beyer/M +Bharat/M +Bhopal/M +Bhutan/M +Bhutanese/M +Bhutto/M +Bi/M +Bialystok/M +Bianca/M +Bib +Bible/MS +Bic/M +Biddle/M +Biden/M +Bierce/M +BigQuery/M +Bigfoot/M +Biggles/M +Biko/M +Bilbao/M +Bilbo/M +Bill/MJ +Billie/M +Billings/M +Billy/M +Bimini/M +Binghamton/M +Biogen/M +Bioko/M +Bird/M +Birdseye/M +Birkenstock/M +Birmingham/M +Biro/M +Biscay/M +Biscayne/M +Bishkek/M +Bishop/M +Bismarck/M +Bismark/M +Bisquick/M +Bissau/M +BitTorrent/M +Bizet/M +Bjerknes/M +Bjork/M +Bk/M +BlackBerry/M +Blackbeard/M +Blackburn/M +Blackfeet/M +Blackfoot/M +Blackpool/M +Blacksburg/M +Blackshirt/M +Blackstone/M +Blackwell/M +Blaine/M +Blair/M +Blake/M +Blanca/M +Blanchard/M +Blanche/M +Blankenship/M +Blantyre/M +Blatz/M +Blavatsky/M +Blenheim/M +Blevins/M +Bligh/M +Bloch/M +Blockbuster/M +Bloemfontein/M +Blondel/M +Blondie/M +Bloom/MR +Bloomer/M +Bloomfield/M +Bloomingdale/M +Bloomington/M +Bloomsburg/M +Bloomsbury/M +Blu +Blucher/M +Bluebeard/M +Bluetooth/M +Blvd +Blythe/M +Boadicea +Boas/M +Bob/M +Bobbi/M +Bobbie/M +Bobbitt/M +Bobby/M +Boccaccio/M +Bodhidharma/M +Bodhisattva/M +Bodleian +Boeing/M +Boeotia/M +Boeotian/M +Boer/SM +Boethius/M +Bogart/M +Bogota/M +Bohemia/M +Bohemian/SM +Bohr/M +Boise/M +Bojangles/M +Boleyn/M +Bolivar/M +Bolivia/M +Bolivian/MS +Bollywood/M +Bologna/M +Bolshevik/SM +Bolsheviki +Bolshevism/M +Bolshevist/M +Bolshoi/M +Bolton/M +Boltzmann/M +Bombay/M +Bonaparte/M +Bonaventure/M +Bond/M +Bonhoeffer/M +Boniface/M +Bonita/M +Bonn/MR +Bonner/M +Bonneville/M +Bonnie/M +Bono/M +Booker/M +Boole/M +Boolean/M +Boone/M +Bootes/M +Booth/M +Bordeaux/M +Borden/M +Bordon/M +Boreas/M +Borg/SM +Borges/M +Borgia/M +Borglum/M +Boris/M +Bork/M +Borlaug/M +Born/M +Borneo/M +Borobudur/M +Borodin/M +Boru/M +Bosch/M +Bose/M +Bosnia/M +Bosnian +Bosporus/M +Boston/MS +Bostonian/M +Boswell/M +Botha +Botox +Botswana/M +Botticelli/M +Boulder/M +Boulez/M +Bourbaki/M +Bourbon/SM +Bournemouth/M +Bovary/M +Bowditch/M +Bowell/M +Bowen/M +Bowers/M +Bowery/M +Bowie/M +Bowman/M +Boyd/M +Boyer/M +Boyle/M +Br/MT +Brad/MY +Bradbury/M +Braddock/M +Bradenton/M +Bradford/M +Bradley/M +Bradly/M +Bradshaw/M +Bradstreet/M +Brady/M +Bragg/M +Brahe/M +Brahma/MS +Brahmagupta/M +Brahman/MS +Brahmani +Brahmanism/SM +Brahmaputra/M +Brahms/M +Braille/MS +Brain/M +Brampton/M +Bran/M +Branch/M +Brandeis/M +Branden/M +Brandenburg/M +Brandi/M +Brandie/M +Brando/M +Brandon/M +Brandt/M +Brandy/M +Brant/M +Braque/M +Brasilia/M +Bratislava/M +Brattain/M +Bray/M +Brazil/M +Brazilian/MS +Brazos/M +Brazzaville/M +Breakspear/M +Breathalyzer +Brecht/M +Breckenridge/M +Bremen/M +Bremerton/M +Brenda/M +Brendan/M +Brennan/M +Brenner/M +Brent/M +Brenton/M +Brest/M +Bret/M +Breton/M +Brett/M +Brewer/M +Brewster/M +Brexit +Brezhnev/M +Brian/M +Briana/M +Brianna/M +Brice/M +Bridalveil/M +Bridgeport/M +Bridger/M +Bridges/M +Bridget/M +Bridgetown/M +Bridgett/M +Bridgette/M +Bridgman/M +Brie/SM +Brigadoon/M +Briggs/M +Brigham/M +Bright/M +Brighton/M +Brigid/M +Brigitte/M +Brillo/M +Brillouin +Brinkley/M +Brisbane/M +Bristol/M +Brit/SM +Britain/M +Britannia/M +Britannic/M +Britannica/M +Briticism/SM +British/MRZ +Britisher/M +Britney/M +Briton/MS +Britt/MN +Brittany/SM +Britten/M +Brittney/M +Brno/M +Broadway/SM +Brobdingnag/M +Brobdingnagian/M +Brock/M +Brokaw/M +Bronson/M +Bronte/M +Brontosaurus +Bronx/M +Brooke/MS +Brooklyn/M +Brooks/M +Bros +Brown/MG +Browne/M +Brownian/M +Brownie/S +Browning/M +Brownshirt/M +Brownsville/M +Brubeck/M +Bruce/M +Bruckner/M +Bruegel +Brummel/M +Brunei/M +Bruneian/MS +Brunelleschi/M +Brunhilde/M +Bruno/M +Brunswick/M +Brussels/M +Brut/M +Brutus/M +Bryan/M +Bryant/M +Bryce/M +Brynner/M +Bryon/M +Brzezinski/M +Btu/M +Buber/M +Buchanan/M +Bucharest/M +Buchenwald/M +Buchwald/M +Buck/M +Buckingham/M +Buckley/M +Buckner/M +Bud/M +Budapest/M +Buddha/SM +Buddhism/SM +Buddhist/SM +Buddy/M +Budweiser/M +Buffalo/M +Buffy/M +Buford/M +Bugatti/M +Bugzilla/M +Buick/M +Bujumbura/M +Bukhara/M +Bukharin/M +Bulawayo/M +Bulfinch/M +Bulganin/M +Bulgar/M +Bulgari/M +Bulgaria/M +Bulgarian/SM +Bullock/M +Bullwinkle/M +Bultmann/M +Bumppo/M +Bunche/M +Bundesbank/M +Bundestag/M +Bunin/M +Bunker/M +Bunsen/M +Bunuel/M +Bunyan/M +Burbank/M +Burberry/M +Burch/M +Burger/M +Burgess/M +Burgoyne/M +Burgundian/M +Burgundy/SM +Burke/M +Burks/M +Burl/M +Burlington/M +Burma/M +Burmese/M +Burnett/M +Burns/M +Burnside/M +Burr/M +Burris/M +Burroughs/M +Bursa/M +Burt/M +Burton/M +Burundi/M +Burundian/MS +Busch/M +Bush/M +Bushido/M +Bushnell/M +Butler/M +Butterfingers/M +Buxtehude/M +Byblos/M +Byers/M +Byrd/M +Byron/M +Byronic/M +Byzantine/MS +Byzantium/M +C/SM +CA +CAD/M +CAI +CAM +CAP +CARE +CATV +CB +CBC/M +CBS/M +CCTV +CCU +CD/SM +CDC +CDT +CEO/M +CF +CFC/M +CFO +CGI +CIA/M +CID +CNN/M +CNS/M +CO/M +COBOL/SM +COD +COL +COLA +COVID +CPA/M +CPI/M +CPO +CPR/M +CPU/M +CRT/SM +CSS/M +CST/M +CT/M +CV +CVS/M +CZ +Ca/M +Cabernet/M +Cabot/M +Cabral/M +Cabrera/M +Cabrini/M +Cadette +Cadillac/M +Cadiz/M +Caedmon/M +Caerphilly/M +Caesar/SM +Cage/M +Cagney/M +Cahokia/M +Caiaphas/M +Cain/SM +Cairo/M +Caitlin/M +Cajun/MS +Cal/M +Calais/M +Calcutta/M +Calder/M +Calderon/M +Caldwell/M +Caleb/M +Caledonia/M +Calexico/M +Calgary/M +Calhoun/M +Cali/M +Caliban/M +Calif +California/M +Californian/SM +Caligula/M +Callaghan/M +Callahan/M +Callao/M +Callas/M +Callie/M +Calliope/M +Callisto/M +Caloocan/M +Calvary/M +Calvert/M +Calvin/M +Calvinism/MS +Calvinist/MS +Calvinistic +Camacho/M +Camarillo/M +Cambodia/M +Cambodian/SM +Cambrian/SM +Cambridge/M +Camden/M +Camel/M +Camelopardalis/M +Camelot/MS +Camembert/MS +Cameron/M +Cameroon/SM +Cameroonian/MS +Camilla/M +Camille/M +Camoens/M +Campanella/M +Campbell/M +Campinas/M +Campos/M +Camry/M +Camus/M +Can/M +Canaan/M +Canaanite/MS +Canad +Canada/M +Canadian/SM +Canadianism +Canaletto/M +Canaries/M +Canaveral/M +Canberra/M +Cancer/SM +Cancun/M +Candace/M +Candice/M +Candide/M +Candy/M +Cannes/M +Cannon/M +Canon/M +Canopus/M +Cantabrigian/M +Canterbury/M +Canton/M +Cantonese/M +Cantor/M +Cantrell/M +Cantu/M +Canute/M +Capablanca/M +Capek/M +Capella/M +Capet/M +Capetian/M +Capetown/M +Caph/M +Capistrano/M +Capitol/SM +Capitoline/M +Capone/M +Capote/M +Capra/M +Capri/M +Capricorn/MS +Capt +Capuchin/M +Capulet/M +Cara/M +Caracalla/M +Caracas/M +Caravaggio/M +Carboloy/M +Carbondale/M +Carboniferous/M +Carborundum/M +Cardenas/M +Cardiff/M +Cardin/M +Cardozo/M +Carey/M +Carib/MS +Caribbean/MS +Carina/M +Carissa/M +Carl/M +Carla/M +Carlene/M +Carlin/M +Carlo/MS +Carlos/M +Carlsbad/M +Carlson/M +Carlton/M +Carly/M +Carlyle/M +Carmela/M +Carmella/M +Carmelo/M +Carmen/M +Carmichael/M +Carmine/M +Carnap/M +Carnation/M +Carnegie/M +Carney/M +Carnot/M +Carol/M +Carole/M +Carolina/M +Caroline/M +Carolingian/M +Carolinian/M +Carolyn/M +Carpathian/SM +Carpathians/M +Carpenter/M +Carr/M +Carranza/M +Carrie/RM +Carrier/M +Carrillo/M +Carroll/M +Carson/M +Carter/M +Cartersville/M +Cartesian/M +Carthage/M +Carthaginian/MS +Cartier/M +Cartwright/M +Caruso/M +Carver/M +Cary/M +Casablanca/M +Casals/M +Casandra/M +Casanova/SM +Cascades/M +Case/M +Casey/M +Cash/M +Casio/M +Caspar/M +Casper/M +Caspian/M +Cassandra/SM +Cassatt/M +Cassidy/M +Cassie/M +Cassiopeia/M +Cassius/M +Castaneda/M +Castilian +Castillo/M +Castlereagh/M +Castor/M +Castries/M +Castro/M +Catalan/SM +Catalina/M +Catalonia/M +Catawba/M +Caterpillar/M +Cathay/M +Cather/M +Catherine/M +Cathleen/M +Catholic/MS +Catholicism/MS +Cathryn/M +Cathy/M +Catiline/M +Cato/M +Catskill/SM +Catskills/M +Catt/M +Catullus/M +Caucasian/MS +Caucasoid +Caucasus/M +Cauchy/M +Cavendish/M +Cavour/M +Caxton/M +Cayenne/M +Cayman/M +Cayuga/SM +Cayuse +Cb +Cd/M +Ce/M +Ceausescu/M +Cebu/M +Cebuano/M +Cecelia/M +Cecil/M +Cecile/M +Cecilia/M +Cecily/M +Cedric/M +Celeste/M +Celgene/M +Celia/M +Celina/M +Cellini/M +Celsius/M +Celt/SM +Celtic/SM +Cenozoic/M +Centaurus/M +Centigrade +Central +Cepheid/M +Cepheus/M +Cerberus/M +Cerenkov/M +Ceres/M +Cerf/M +Cervantes/M +Cesar/M +Cesarean/M +Cessna/M +Cetus/M +Ceylon/M +Ceylonese +Cezanne/M +Cf/M +Ch'in/M +Ch/N +Chablis/M +Chad/M +Chadian/MS +Chadwick/M +Chagall/M +Chaitanya/M +Chaitin/M +Chaldea +Chaldean/M +Challenger/M +Chalmers +Chamberlain/M +Chambers/M +Chambersburg/M +Champaign/M +Champlain/M +Champollion/M +Chan/M +Chance/M +Chancellorsville/M +Chandigarh/M +Chandler/M +Chandon/M +Chandra/M +Chandragupta/M +Chandrasekhar/M +Chanel/M +Chaney/M +Chang/M +Changchun/M +Changsha/M +Chantilly/M +Chaplin/M +Chaplinesque +Chapman/M +Chappaquiddick/M +Chapultepec/M +Charbray/M +Chardonnay/M +Charity/M +Charlemagne/M +Charlene/M +Charles/M +Charleston/MS +Charley/M +Charlie/M +Charlotte/M +Charlottesville/M +Charlottetown/M +Charmaine/M +Charmin/M +Charolais/M +Charon/M +Chartism/M +Chartres/M +Charybdis/M +Chase/M +Chasity/M +Chateaubriand/M +Chattahoochee/M +Chattanooga/M +Chatterley/M +Chatterton/M +Chaucer/M +Chauncey/M +Chautauqua/M +Chavez/M +Chayefsky/M +Che/M +Chechen/M +Chechnya/M +Cheddar/M +Cheer/M +Cheerios/M +Cheetos/M +Cheever/M +Chekhov/M +Chekhovian +Chelsea/M +Chelyabinsk/M +Chen/M +Cheney/M +Chengdu/M +Chennai/M +Cheops/M +Cheri/M +Cherie/M +Chernenko/M +Chernobyl/M +Chernomyrdin/M +Cherokee/MS +Cherry/M +Cheryl/M +Chesapeake/M +Cheshire/M +Chester/M +Chesterfield/M +Chesterton/M +Chevalier/M +Cheviot/M +Chevrolet/M +Chevron/M +Chevy/M +Cheyenne/SM +Chi/M +Chianti/MS +Chiba/M +Chibcha/M +Chicago/M +Chicagoan/M +Chicana/M +Chicano/M +Chickasaw/MS +Chiclets/M +Chico/M +Chihuahua/MS +Chile/M +Chilean/MS +Chimborazo/M +Chimera/MS +Chimu/M +Chin/M +China/M +Chinatown/M +Chinese/M +Chinook/MS +Chipewyan/M +Chippendale/M +Chippewa/SM +Chiquita/M +Chirico/M +Chisholm/M +Chisinau/M +Chittagong/M +Chivas/M +Chloe/M +Choctaw/SM +Chomsky/M +Chongqing/M +Chopin/M +Chopra/M +Chou/M +Chretien/M +Chris/M +Christ/MS +Christa/M +Christchurch/M +Christendom/MS +Christensen/M +Christi/M +Christian/SM +Christianity/SM +Christianize +Christie/M +Christina/M +Christine/M +Christlike +Christmas/MS +Christmastide/MS +Christmastime/MS +Christoper/M +Christopher/M +Chromebook/MS +Chronicles +Chrysler/M +Chrysostom/M +Chrystal/M +Chuck/M +Chukchi/M +Chumash/M +Chung/M +Church/M +Churchill/M +Churriguera/M +Chuvash/M +Ci/M +Cicero/M +Cid/M +Cimabue/M +Cincinnati/M +Cinderella/MS +Cindy/M +CinemaScope/M +Cinerama/M +Cipro/M +Circe/M +Cisco/M +Citibank/M +Citigroup/M +Citroen/M +Cl/MV +Claiborne/M +Clair/M +Claire/M +Clairol/M +Clancy/M +Clapeyron/M +Clapton/M +Clara/M +Clare/M +Clarence/M +Clarendon/M +Clarice/M +Clarissa/M +Clark/M +Clarke/M +Clarksville/M +Claude/M +Claudette/M +Claudia/M +Claudine/M +Claudio/M +Claudius/M +Claus/M +Clausewitz/M +Clausius/M +Clay/M +Clayton/M +Clearasil/M +Clem/XM +Clemenceau/M +Clemens/M +Clement/MS +Clementine/M +Clements/M +Clemons/M +Clemson/M +Cleo/M +Cleopatra/M +Cleveland/M +Cliburn/M +Cliff/M +Clifford/M +Clifton/M +Cline/M +Clint/M +Clinton/M +Clio/M +Clive/M +Clojure/M +Clorets/M +Clorox/M +Closure/M +Clotho/M +Clouseau/M +Clovis/M +Clyde/M +Clydesdale/M +Clytemnestra/M +Cm/M +Cmdr +Co/M +Cobain/M +Cobb/M +Cochabamba/M +Cochin/M +Cochise/M +Cochran/M +Cockney/M +Cocteau/M +Cod +Cody/M +Coffey/M +Cognac/M +Cohan/M +Cohen/M +Coimbatore/M +Cointreau/M +Coke/SM +Col/M +Colbert/M +Colby/M +Cole/M +Coleen/M +Coleman/M +Coleridge/M +Colette/M +Colfax/M +Colgate/M +Colin/M +Colleen/M +Collier/M +Collin/SM +Collins/M +Colo +Cologne/M +Colombia/M +Colombian/MS +Colombo/M +Colon/M +Coloradan/SM +Colorado/M +Coloradoan +Colosseum/M +Colt/M +Coltrane/M +Columbia/M +Columbine/M +Columbus/M +Com +Comanche/MS +Combs/M +Comdr +Comintern/M +Commandment +Commons/M +Commonwealth +Communion/SM +Communism +Communist/SM +Como/M +Comoran +Comoros/M +Compaq/M +Compton/M +CompuServe/M +Comte/M +Conakry/M +Conan/M +Concepcion/M +Concetta/M +Concord/SM +Concorde/M +Condillac/M +Condorcet/M +Conestoga/M +Confederacy/M +Confederate/MS +Confucian/SM +Confucianism/MS +Confucius/M +Cong/M +Congo/M +Congolese/M +Congregational +Congregationalist/MS +Congress/MS +Congressional +Congreve/M +Conley/M +Conn/MR +Connecticut/M +Connellsville/M +Connemara/M +Conner/M +Connery/M +Connie/M +Connolly/M +Connors/M +Conrad/M +Conrail/M +Conroe/M +Conservative +Constable/M +Constance/M +Constantine/M +Constantinople/M +Constitution +Consuelo/M +Continent/M +Continental/M +Contreras/M +Conway/M +Cook/M +Cooke/M +Cooley/M +Coolidge/M +Cooper/M +Cooperstown/M +Coors/M +Copacabana/M +Copeland/M +Copenhagen/M +Copernican/M +Copernicus/M +Copland/M +Copley/M +Copperfield/M +Coppertone/M +Coppola/M +Coptic/M +Cora/M +Cordelia/M +Cordilleras/M +Cordoba/M +Corey/M +Corfu/M +Corina/M +Corine/M +Corinne/M +Corinth/M +Corinthian/MS +Corinthians/M +Coriolanus/M +Coriolis/M +Cork +Corleone/M +Cormack/M +Corneille/M +Cornelia/M +Cornelius/M +Cornell/M +Corning/M +Cornish/MS +Cornwall/M +Cornwallis/M +Coronado/M +Corot/M +Corp +Correggio/M +Corrine/M +Corsica/M +Corsican/M +Cortes/MS +Cortland/M +Corvallis/M +Corvette/M +Corvus/M +Cory/M +Cosby/M +CosmosDB/M +Cossack/M +Costco/M +Costello/M +Costner/M +Cote/M +Cotonou/M +Cotopaxi/M +Cotswold/M +Cotton/M +Coulomb/M +Coulter/M +Couperin/M +Courbet/M +Courtney/M +Cousteau/M +Coventry/SM +Covington/M +Coward/M +Cowell/M +Cowley/M +Cowper/M +Cox/M +Coy/M +Coyle/M +Cozumel/M +Cpl +Cr/MT +Crabbe/M +Craft/M +Craig/M +Cranach/M +Crane/M +Cranmer/M +Crater/M +Crawford/M +Cray/M +Crayola/M +Creation/M +Creator/M +Crecy/M +Cree/DSM +Creek/SM +Creighton/M +Creole/SM +Creon/M +Cressida/M +Crest/M +Cretaceous/M +Cretan/SM +Crete/M +Crichton/M +Crick/M +Crimea/M +Crimean/M +Criollo/M +Crisco/M +Cristina/M +Croat/SM +Croatia/M +Croatian/MS +Croce/M +Crockett/M +Croesus/M +Cromwell/M +Cromwellian/M +Cronin/M +Cronkite/M +Cronus/M +Crookes/M +Crosby/M +Cross/M +Crow/SM +Crowley/M +Crucifixion/MS +Cruikshank/M +Cruise/M +Crusades's +Crusoe/M +Crux/M +Cruz/M +Cryptozoic/M +Crystal/M +Csonka/M +Ct +Ctesiphon/M +Cthulhu/M +Cu/M +Cuba/M +Cuban/SM +Cuchulain/M +Cuisinart/M +Culbertson/M +Cullen/M +Cumberland/M +Cummings/M +Cunard/M +Cunningham/M +Cupid/M +Curacao/M +Curie/M +Curitiba/M +Currier/M +Curry/RM +Curt/M +Curtis/M +Custer/M +Cuvier/M +Cuzco/M +Cybele/M +Cyclades/M +Cyclopes/M +Cyclops/M +Cygnus/M +Cymbeline/M +Cynthia/M +Cyprian/M +Cypriot/MS +Cyprus/M +Cyrano/M +Cyril/M +Cyrillic/M +Cyrus/M +Czech/M +Czechia/M +Czechoslovak +Czechoslovakia/M +Czechoslovakian/SM +Czechs +Czerny/M +D/M +DA/M +DAR +DAT/M +DBMS/M +DC/M +DD/M +DDS/M +DDT/S +DE +DEA +DEC/SD +DH +DHS +DI +DJ +DMCA +DMD/M +DMZ +DNA/M +DOA +DOB +DOD +DOE +DOS/M +DOT +DP/SM +DPT +DST +DTP +DUI +DVD/S +DVR/SM +DWI +Dachau/M +Dacron/SM +Dada/M +Dadaism/M +Daedalus/M +Daguerre/M +Dagwood/M +Dahomey/M +Daimler/M +Daisy/M +Dakar/M +Dakota/SM +Dakotan/M +Dalai +Dale/M +Daley/M +Dali/M +Dalian/M +Dallas/M +Dalmatia/M +Dalmatian/SM +Dalton/M +Damascus/M +Dame/MN +Damian/M +Damien/M +Damion/M +Damocles/M +Damon/M +Dan/M +Dana/M +Danae/M +Danbury/M +Dane/SM +Danelaw/M +Dangerfield/M +Danial/M +Daniel/SM +Danielle/M +Daniels/M +Danish/M +Dannie/M +Danny/M +Danone/M +Dante/M +Danton/M +Danube/M +Danubian/M +Danville/M +Daphne/M +Darby/M +Darcy/M +Dardanelles/M +Dare/M +Daren/M +Darfur/M +Darin/M +Dario/M +Darius/M +Darjeeling/M +Darla/M +Darlene/M +Darling/M +Darnell/M +Darrel/M +Darrell/M +Darren/M +Darrin/M +Darrow/M +Darryl/M +Darth/M +Dartmoor/M +Dartmouth/M +Darvon/M +Darwin/M +Darwinian/M +Darwinism/SM +Darwinist +Daryl/M +Datamation +Daugherty/M +Daumier/M +Davao/M +Dave/M +Davenport/M +David/MS +Davidson/M +Davies/M +Davis/M +Davy/SM +Dawes/M +Dawkins +Dawn/M +Dawson/M +Day/M +Dayan +Dayton/M +DeGeneres/M +DeKalb/M +Deadhead/M +Dean/M +Deana/M +Deandre/M +Deann/M +Deanna/M +Deanne/M +Death/M +Debbie/M +Debby/M +Debian/M +Debora/M +Deborah/M +Debouillet/M +Debra/M +Debs/M +Debussy/M +Dec/M +Decalogue/M +Decatur/M +Decca/M +Deccan/M +December/SM +Decker/M +Dedekind/M +Dee/M +Deena/M +Deere/M +Defoe/M +Degas/M +Deidre/M +Deimos/M +Deirdre/M +Deity +Dejesus/M +Del/M +Delacroix/M +Delacruz/M +Delaney/M +Delano/M +Delaware/MS +Delawarean/SM +Delbert/M +Deleon/M +Delgado/M +Delhi/M +Delia/M +Delibes/M +Delicious/M +Delilah/M +Delilahs +Delius/M +Dell/M +Della/M +Delmar/M +Delmarva/M +Delmer/M +Delmonico/M +Delores/M +Deloris/M +Delphi/M +Delphic/M +Delphinus/M +Delta/M +Deltona/M +Dem/G +Demavend/M +Demerol/M +Demeter/M +Demetrius/M +Deming/M +Democrat/SM +Democratic +Democritus/M +Demosthenes/M +Dempsey/M +Dena/M +Denali +Deneb/M +Denebola/M +Deng/M +Denis/M +Denise/M +Denmark/M +Dennis/M +Denny/M +Denton/M +Denver/M +Deon/M +Depp/M +Derby/M +Derek/M +Derick/M +Dermot/M +Derrick/M +Derrida/M +Descartes/M +Desdemona/M +Desiree/M +Desmond/M +Detroit/M +Deuteronomy/M +Devanagari/M +Devi/M +Devin/M +Devon/M +Devonian/M +Dewar/M +Dewayne/M +Dewey/M +Dewitt/M +Dexedrine/M +Dexter/M +Dhaka/M +Dhaulagiri/M +Di/SM +DiCaprio/M +DiMaggio/M +Diaghilev/M +Dial/M +Diana/M +Diane/M +Diann/M +Dianna/M +Dianne/M +Dias +Diaspora/MS +Dick/XM +Dickens/M +Dickensian +Dickerson/M +Dickinson/M +Dickson/M +Dictaphone/SM +Diderot/M +Dido/M +Didrikson/M +Diefenbaker/M +Diego/M +Diem/M +Dietrich/M +Dijkstra/M +Dijon/M +Dilbert/MS +Dillard/M +Dillinger/M +Dillon/M +Dina/M +Dinah/M +Dino/M +Diocletian/M +Diogenes/M +Dion/M +Dionne/M +Dionysian/M +Dionysus/M +Diophantine/M +Dior/M +Dipper/M +Dir +Dirac/M +Dirichlet/M +Dirk/M +Dis/M +Disney/M +Disneyland/M +Disraeli/M +Divine/M +Diwali/M +Dix/M +Dixie/M +Dixiecrat/M +Dixieland/SM +Dixon/M +Django/M +Djibouti/M +Dmitri/M +Dnepropetrovsk/M +Dniester/M +Dobbin/M +Doberman/M +Dobro/M +Doctor +Doctorow/M +Dodge/M +Dodgson/M +Dodoma/M +Dodson/M +Doe/M +Doha/M +Dolby/M +Dole/M +Dollie/M +Dolly/M +Dolores/M +Domesday/M +Domingo/M +Dominguez/M +Dominic/M +Dominica/M +Dominican/MS +Dominick/M +Dominion +Dominique/M +Domitian/M +Don/SM +Dona/M +Donahue/M +Donald/M +Donaldson/M +Donatello/M +Donetsk/M +Donizetti/M +Donn/MR +Donna/M +Donne/M +Donnell/M +Donner/M +Donnie/M +Donny/M +Donovan/M +Dooley/M +Doolittle/M +Doonesbury/M +Doppler/M +Dora/M +Dorcas/M +Doreen/M +Dorian/M +Doric/M +Doris/M +Doritos/M +Dorothea/M +Dorothy/M +Dorset/M +Dorsey/M +Dorthy/M +Dortmund/M +Dostoevsky/M +Dot/M +Dothan/M +Dotson/M +Douala/M +Douay/M +Doubleday/M +Doug/M +Douglas/M +Douglass/M +Douro/M +Dover/M +Dow/M +Downs/M +Downy/M +Doyle/M +Dr +Draco/M +Draconian/M +Dracula/M +Drake/M +Dramamine/SM +Drambuie/M +Drano/M +Dravidian/M +Dreiser/M +Dresden/M +Drew/M +Dreyfus/M +Dristan/M +Dropbox/M +Drudge/M +Drupal/M +Dryden/M +Dschubba/M +Du +DuPont/M +Duane/M +Dubai/M +Dubcek/M +Dubhe/M +Dublin/M +Dubrovnik/M +Dubuque/M +Duchamp/M +Dudley/M +Duffy/M +Duisburg/M +Duke/M +Dulles/M +Duluth/M +Dumas/M +Dumbledore/M +Dumbo/M +Dunant/M +Dunbar/M +Duncan/M +Dundee +Dunedin/M +Dunkirk/M +Dunlap/M +Dunn/M +Dunne/M +Duracell/M +Duran/M +Durant/M +Durante/M +Durban/M +Durer/M +Durex/M +Durham/MS +Durkheim/M +Duroc/M +Durocher/M +Duse/M +Dushanbe/M +Dusseldorf/M +Dustbuster/M +Dustin/M +Dusty/M +Dutch/M +Dutchman/M +Dutchmen/M +Dutchwoman +Duvalier/M +Dvina/M +Dvorak/M +Dwayne/M +Dwight/M +Dy/M +Dyer/M +Dylan/M +DynamoDB/M +Dyson/M +Dzerzhinsky/M +Dzungaria/M +E/SM +EC +ECG/M +ECMAScript/M +EDP/M +EDT +EEC/M +EEG/M +EEO +EEOC +EFL +EFT +EKG/M +ELF/M +EM +EMT +ENE/M +EOE +EPA/M +ER +ERA +ESE/M +ESL +ESP/M +ESPN/M +ESR +EST/M +ET +ETA +ETD +EU +EULA/S +Eakins/M +Earhart/M +Earl/M +Earle/M +Earlene/M +Earline/M +Earnest/M +Earnestine/M +Earnhardt/M +Earp/M +East/SZMR +Easter/M +Eastern/R +Eastman/M +Eastwood/M +Eaton/M +Eben/M +Ebeneezer/M +Ebert/M +Ebola/M +Ebonics/M +Ebony/M +Ebro/M +Ecclesiastes/M +Eco/M +Ecstasy +Ecuador/M +Ecuadoran/SM +Ecuadorean +Ecuadorian/SM +Ed/MNX +Edam/SM +Edda/M +Eddie/M +Eddington/M +Eddy/M +Eden/M +Edgar/M +Edgardo/M +Edinburgh/M +Edison/M +Edith/M +Edmond/M +Edmonton/M +Edmund/M +Edna/M +Edsel/M +Eduardo/M +Edward/SM +Edwardian/M +Edwardo/M +Edwards/M +Edwin/M +Edwina/M +Eeyore/M +Effie/M +Efrain/M +Efren/M +Eggo/M +Egypt/M +Egyptian/MS +Egyptology/M +Ehrenberg/M +Ehrlich/M +Eichmann/M +Eiffel/M +Eileen/M +Einstein/MS +Eire/M +Eisenhower/M +Eisenstein/M +Eisner/M +Elaine/M +Elam/M +Elanor/M +Elasticsearch/M +Elastoplast/M +Elba/M +Elbe/M +Elbert/M +Elbrus/M +Eldersburg/M +Eldon/M +Eleanor/M +Eleazar/M +Electra/M +Elena/M +Elgar/M +Eli/M +Elias/M +Elijah/M +Elinor/M +Eliot/M +Elisa/M +Elisabeth/M +Elise/M +Eliseo/M +Elisha/M +Eliza/M +Elizabeth/M +Elizabethan/SM +Elizabethtown/M +Elkhart/M +Ella/M +Ellen/M +Ellesmere/M +Ellie/M +Ellington/M +Elliot/M +Elliott/M +Ellis/M +Ellison/M +Elma/M +Elmer/M +Elmira/M +Elmo/M +Elnath/M +Elnora/M +Elohim/M +Eloise/M +Eloy/M +Elroy/M +Elsa/M +Elsie/M +Elsinore/M +Eltanin/M +Elton/M +Elul/M +Elva/M +Elvia/M +Elvin/M +Elvira/M +Elvis/M +Elway/M +Elwood/M +Elyria/M +Elysee/M +Elysian/M +Elysium/SM +Emacs/M +Emanuel/M +Emerson/M +Emery/M +Emil/M +Emile/M +Emilia/M +Emilio/M +Emily/M +Eminem/M +Eminence +Emma/M +Emmanuel/M +Emmett/M +Emmy/M +Emory/M +Encarta/M +Endymion/M +Eng/M +Engels/M +England/M +English/MRS +Englishman/M +Englishmen/M +Englishwoman/M +Englishwomen/M +Enid/M +Enif/M +Eniwetok/M +Enkidu/M +Enoch/M +Enos/M +Enrico/M +Enrique/M +Enron/M +Enterprise/M +Eocene/M +Epcot/M +Ephesian/MS +Ephesus/M +Ephraim/M +Epictetus/M +Epicurean/M +Epicurus/M +Epimethius/M +Epiphany/SM +Episcopal +Episcopalian/MS +Epistle +Epsom/M +Epson/M +Epstein/M +Equuleus/M +Er/M +Erasmus/M +Erato/M +Eratosthenes/M +Erebus/M +Erector/M +Erewhon/M +Erhard/M +Eric/M +Erica/M +Erich/M +Erick/M +Ericka/M +Erickson/M +Eridanus/M +Erie/M +Erik/M +Erika/M +Erin/M +Eris/MS +Eritrea/M +Eritrean/SM +Erlang/M +Erlenmeyer/M +Erma/M +Erna/M +Ernest/M +Ernestine/M +Ernesto/M +Ernie/M +Ernst/M +Eros/MS +Errol/M +Erse/M +ErvIn/M +Erwin/M +Esau/M +Escher/M +Escherichia/M +Escondido +Eskimo/MS +Esmeralda/M +Esperanto/M +Esperanza/M +Espinoza/M +Esq/M +Esquire/MS +Essen/M +Essene/M +Essequibo/M +Essex/M +Essie/M +Establishment +Esteban/M +Estela/M +Estella/M +Estelle/M +Ester/M +Esterhazy/M +Estes/M +Esther/M +Estonia/M +Estonian/SM +Estrada/M +Ethan/M +Ethel/M +Ethelred/M +Ethernet/M +Ethiopia/M +Ethiopian/SM +Etna/M +Eton/M +Etruria/M +Etruscan/M +Etta/M +Eu/M +Eucharist/MS +Eucharistic +Euclid/M +Eugene/M +Eugenia/M +Eugenie/M +Eugenio/M +Eula/M +Euler/M +Eumenides/M +Eunice/M +Euphrates/M +Eur +Eurasia/M +Eurasian/MS +Euripides/M +Eurodollar/SM +Europa/M +Europe/M +European/MS +Eurydice/M +Eustachian/M +Eustis/M +Euterpe/M +Eva/M +Evan/SM +Evangelical +Evangelina/M +Evangeline/M +Evangelist/M +Evans/M +Evansville/M +Eve/M +Evelyn/M +Evenki/M +EverReady/M +Everest/M +Everett/M +Everette/M +Everglades/M +Evert/M +Evian/M +Evita/M +Ewing/M +Excalibur/M +Excedrin/M +Excellency/SM +Exchequer +Exercycle/M +Exocet/M +Exodus/M +Exxon/M +Eyck/M +Eyre/M +Eysenck/M +Ezekiel/M +Ezra/M +F/MD +FAA +FAQ/SM +FBI/M +FCC +FD +FDA +FDIC/M +FDR/M +FHA/M +FICA/M +FIFO +FL +FM/SM +FNMA/M +FOFL +FORTRAN/M +FPO +FSF/M +FSLIC +FTC +FUD/S +FWD +FWIW +FY +FYI +Faberge/M +Fabian/MS +Facebook/M +Faeroe/M +Fafnir/M +Fagin/M +Fahd/M +Fahrenheit/M +Fairbanks/M +Fairfield/M +Fairhope/M +Faisal/M +Faisalabad/M +Faith/M +Fajardo/M +Falasha/M +Falkland/SM +Falklands/M +Fallopian/M +Falstaff/M +Falwell/M +Fannie/M +Fanny/M +Faraday/M +Fargo/M +Farley/M +Farmer/M +Farmington/M +Farragut/M +Farrakhan/M +Farrell/M +Farrow/M +Farsi/M +Fassbinder/M +Fatah/M +Fates/M +Father/SM +Fatima/M +Fatimid/M +Faulkner/M +Faulknerian/M +Fauntleroy/M +Faust/M +Faustian/M +Faustino/M +Faustus/M +Fawkes/M +Fay/M +Faye/M +Fayetteville/M +Fe/M +Feb/M +February/SM +Fed/SM +FedEx/M +Federal/MS +Federalist/M +Federico/M +Feds/M +Felecia/M +Felice/M +Felicia/M +Felicity/M +Felipe/M +Felix/M +Fellini/M +Fenian/M +Ferber/M +Ferdinand/M +Fergus/M +Ferguson/M +Ferlinghetti/M +Fermat/M +Fermi/M +Fern/M +Fernandez/M +Fernando/M +Ferrari/M +Ferraro/M +Ferrell/M +Ferris/M +Feynman/M +Fez/M +Fiat/M +Fiberglas/M +Fibonacci/M +Fichte/M +Fidel/M +Fido/M +Fielding/M +Fields/M +Figaro/M +Figueroa/M +Fiji/M +Fijian/MS +Filipino/MS +Fillmore/M +Filofax/M +Finch/M +Finland/M +Finlay/M +Finley/M +Finn/SM +Finnbogadottir/M +Finnegan/M +Finnish/M +Fiona/M +Firebase/M +Firefox/M +Firestone/M +Fischer/M +Fisher/M +Fisk/M +Fitch/M +Fitchburg/M +Fitzgerald/M +Fitzpatrick/M +Fitzroy/M +Fizeau/M +Fla +Flagstaff/M +Flanagan/M +Flanders/M +Flathead +Flatt/M +Flaubert/M +Fleischer/M +Fleming/M +Flemish/M +Fletcher/M +Flint/M +Flintstones/M +Flo/M +Flora/M +Florence/M +Florentine/M +Flores/M +Florida/M +Floridan/M +Floridian/SM +Florine/M +Florsheim/M +Flory/M +Flossie/M +Flowers/M +Floyd/M +Flynn/M +Fm/M +Foch/M +Fokker/M +Foley/M +Folgers/M +Folsom/M +Fomalhaut/M +Fonda/M +Foosball/M +Forbes/M +Ford/M +Foreman/M +Forest/MR +Forester/M +Formica/MS +Formosa/M +Formosan/M +Forrest/M +Forster/M +Fortaleza/M +Fosse/M +Foster/M +Fotomat/M +Foucault/M +Fourier/M +Fourneyron/M +Fourth +Fowler/M +Fox/MS +Fr/MD +Fragonard/M +Fran/M +France/SM +Frances/M +Francesca/M +Francine/M +Francis/M +Francisca/M +Franciscan/MS +Francisco/M +Franck/M +Franco/M +Francois/M +Francoise/M +Francophile +Franglais/M +Frank/SM +Frankel/M +Frankenstein/M +Frankfort/M +Frankfurt/MR +Frankfurter/M +Frankie/M +Frankish +Franklin/M +Franks/M +Franny/M +Franz/M +Fraser/M +Frau/MN +Fraulein +Frazier/M +Fred/M +Freda/M +Freddie/M +Freddy/M +Frederic/M +Frederick/M +Fredericksburg/M +Fredericton/M +Fredric/M +Fredrick/M +Freeman/M +Freemason/SM +Freemasonry/SM +Freetown/M +Freida/M +Fremont/M +French/MS +Frenchman/M +Frenchmen/M +Frenchwoman/M +Frenchwomen/M +Freon/M +Fresnel/M +Fresno/M +Freud/M +Freudian/M +Frey/M +Freya/M +Fri/M +Friday/SM +Frieda/M +Friedan/M +Friedman/M +Friedmann/M +Friend/SM +Frigga/M +Frigidaire/M +Frisbee/M +Frisco/M +Frisian/MS +Frito/M +Fritz/M +Frobisher/M +Frodo/M +Froissart/M +Fromm/M +Fronde/M +Frontenac/M +Frost/M +Frostbelt/M +Frunze/M +Fry/M +Frye/M +Fuchs/M +Fuentes/M +Fugger/M +Fuji/M +Fujian/M +Fujitsu/M +Fujiwara/M +Fujiyama/M +Fukuoka/M +Fukuyama/M +Fulani/M +Fulbright/M +Fuller/M +Fullerton/M +Fulton/M +Funafuti/M +Fundy/M +Furies/M +Furman/M +Furtwangler/M +Fushun/M +Fuzhou/M +Fuzzbuster/M +G/MNRB +GA +GAO +GATT/M +GB/M +GCC/M +GDP/M +GE/M +GED +GHQ/M +GHz +GI +GIF +GIGO +GM/M +GMAT +GMO +GMT/M +GNP/M +GNU/M +GOP/M +GP/M +GPA +GPO +GPS +GPU +GSA +GTE/M +GU +GUI/M +Ga/M +Gable/M +Gabon/M +Gabonese/M +Gaborone/M +Gabriel/M +Gabriela/M +Gabrielle/M +Gacrux/M +Gadsden/M +Gaea/M +Gael/SM +Gaelic/M +Gagarin/M +Gage/M +Gaia/M +Gail/M +Gaiman/M +Gaines/M +Gainesville/M +Gainsborough/M +Galahad/SM +Galapagos/M +Galatea/M +Galatia/M +Galatians/M +Galaxy +Galbraith/M +Gale/M +Galen/M +Galibi/M +Galilean/SM +Galilee/M +Galileo/M +Gall/M +Gallagher/M +Gallegos/M +Gallic/M +Gallicism/SM +Gallo/M +Galloway/M +Gallup/M +Galois/M +Galsworthy/M +Galvani/M +Galveston/M +Gama +Gamay/M +Gambia/M +Gambian/SM +Gamble/M +Gamow/M +Gandalf/M +Gandhi/M +Gandhian/M +Ganesha/M +Ganges/M +Gangtok/M +Gansu/M +Gantry/M +Ganymede/M +Gap/M +Garbo/M +Garcia/M +Gardner/M +Gareth/M +Garfield/M +Garfunkel/M +Gargantua/M +Garibaldi/M +Garland/M +Garner/M +Garrett/M +Garrick/M +Garrison/M +Garry/M +Garth/M +Garvey/M +Gary/M +Garza/M +Gascony/M +Gasser/M +Gastonia/M +Gastroenterology +Gates/M +Gatling/M +Gatorade/M +Gatsby/M +Gatun/M +Gauguin/M +Gaul/SM +Gaulish +Gauss/M +Gaussian/M +Gautama/M +Gautier/M +Gavin/M +Gawain/M +Gay/M +Gayle/M +Gaza/M +Gaziantep/M +Gd/M +Gdansk/M +Ge/M +Geffen/M +Gehenna/M +Gehrig/M +Geiger/M +Gelbvieh/M +Geller/M +Gemini/MS +Gen/M +Gena/M +Genaro/M +Gene/M +Genesis/M +Genet/M +Geneva/M +Genevieve/M +Genghis/M +Genoa/SM +Gentoo/M +Gentry/M +Geo/M +Geoffrey/M +George/MS +Georgetown/M +Georgette/M +Georgia/M +Georgian/MS +Georgina/M +Ger/M +Gerald/M +Geraldine/M +Gerard/M +Gerardo/M +Gerber/M +Gere/M +Geritol/M +German/MS +Germanic/M +Germany/M +Geronimo/M +Gerry/M +Gershwin/M +Gertrude/M +Gestapo/SM +Gethsemane/M +Getty/M +Gettysburg/M +Gewurztraminer/M +Ghana/M +Ghanaian +Ghats/M +Ghazvanid/M +Ghent/M +Ghibelline/M +Giacometti/M +Giannini/M +Giauque/M +Gibbon/M +Gibbs/M +Gibraltar/MS +Gibson/M +Gide/M +Gideon/M +Gielgud/M +Gienah/M +Gil/M +Gila/M +Gilbert/M +Gilberto/M +Gilchrist/M +Gilda/M +Gilead/M +Giles/M +Gilgamesh/M +Gill/M +Gillespie/M +Gillette/M +Gilliam/M +Gillian/M +Gilligan/M +Gilman +Gilmore/M +Gilroy/M +Gina/M +Ginger/M +Gingrich/M +Ginny/M +Gino/M +Ginsberg/M +Ginsburg/M +Ginsu/M +Giorgione/M +Giotto/M +Giovanni/M +Giraudoux/M +Giselle/M +Gish/M +GitHub/M +Giuliani/M +Giuseppe/M +Giza/M +Gk +Gladstone/MS +Gladys/M +Glaser/M +Glasgow/M +Glass/M +Glastonbury/M +Glaswegian/SM +Glaxo/M +Gleason/M +Glen/M +Glenda/M +Glendale +Glenlivet/M +Glenn/M +Glenna/M +Gloria/M +Gloucester/M +Glover/M +Gnostic/M +Gnosticism/M +GnuPG +Goa/M +Gobi/M +God/M +Godard/M +Goddard/M +Godel/M +Godhead/M +Godiva/M +Godot/M +Godspeed/SM +Godthaab/M +Godunov/M +Godzilla/M +Goebbels/M +Goering/M +Goethals/M +Goethe/M +Goff/M +Gog/M +Gogol/M +Goiania/M +Golan/M +Golconda/M +Golda/M +Goldberg/M +Golden/M +Goldie/M +Goldilocks/M +Golding/M +Goldman/M +Goldsboro/M +Goldsmith/M +Goldwater/M +Goldwyn/M +Golgi/M +Golgotha/M +Goliath/M +Gomez/M +Gomorrah/M +Gompers/M +Gomulka/M +Gondwanaland/M +Gonzales/M +Gonzalez/M +Gonzalo/M +Good/M +Goodall/M +Goode/M +Goodman/M +Goodrich/M +Goodwill/M +Goodwin/M +Goodyear/M +Google/M +Goolagong/M +Gopher +Gorbachev/M +Gordian/M +Gordimer/M +Gordon/M +Gore/M +Goren/M +Gorey/M +Gorgas/M +Gorgon/M +Gorgonzola/M +Gorky/M +Gospel/MS +Goteborg/M +Goth/M +Gotham/M +Gothic/MS +Goths +Gouda/SM +Gould/M +Gounod/M +Governor +Goya/M +Gr/B +Grable/M +Gracchus/M +Grace/M +Graceland/M +Gracie/M +Graciela/M +Grady/M +Graffias/M +Grafton/M +Graham/M +Grahame/M +Grail/M +Grammy/M +Grampians/M +Granada/M +Grant/M +Grass/M +Graves/M +Gray/M +Grayslake/M +Grecian/M +Greece/M +Greek/SM +Greeley/M +Green/SM +Greene/M +Greenland/M +Greenlandic +Greenpeace/M +Greensboro/M +Greensleeves/M +Greenspan/M +Greenville/M +Greenwich/M +Greer/M +Greg/M +Gregg/M +Gregorian/M +Gregorio/M +Gregory/M +Grenada/M +Grenadian/MS +Grenadines/M +Grendel/M +Grenoble/M +Gresham/M +Greta/M +Gretchen/M +Gretel/M +Gretzky/M +Grey/M +Grieg/M +Griffin/M +Griffith/M +Grimes/M +Grimm/M +Grinch/M +Gris/M +Gromyko/M +Gropius/M +Gross/M +Grosz/M +Grotius/M +Grover/M +Grozny +Grumman/M +Grundy/M +Grunewald/M +Grus/M +Gruyere/SM +Guadalajara/M +Guadalcanal/M +Guadalquivir/M +Guadalupe/M +Guadeloupe/M +Guallatiri/M +Guam/M +Guamanian +Guangdong/M +Guangzhou/M +Guantanamo/M +Guarani/M +Guarnieri/M +Guatemala/M +Guatemalan/MS +Guayama/M +Guayaquil/M +Gucci/M +Guelph/M +Guernsey/MS +Guerra/M +Guerrero/M +Guevara/M +Guggenheim/M +Guiana/M +Guido +Guillermo/M +Guinea/M +Guinean/MS +Guinevere/M +Guinness/M +Guiyang/M +Guizhou/M +Guizot/M +Gujarat/M +Gujarati/M +Gujranwala/M +Gulfport/M +Gullah/M +Gulliver/M +Gumbel/M +Gunther/M +Guofeng/M +Gupta/M +Gurkha/M +Gus/M +Gustav/M +Gustavo/M +Gustavus/M +Gutenberg/M +Guthrie/M +Gutierrez/M +Guy/M +Guyana/M +Guyanese/M +Guzman/M +Gwalior/M +Gwen/M +Gwendoline/M +Gwendolyn/M +Gwyn/M +Gypsy/SM +H/M +HBO/M +HBase/M +HDD +HDMI +HDTV +HF/M +HHS +HI +HIV/M +HM +HMO/M +HMS +HOV +HP/M +HPV +HQ/M +HR +HRH +HS +HSBC/M +HST +HT +HTML/M +HTTP +HUD/M +Ha/M +Haas/M +Habakkuk/M +Haber/M +Hadar/M +Hades/M +Hadoop/M +Hadrian/M +Hafiz/M +Hagar/M +Hagerstown/M +Haggai/M +Hagiographa/M +Hague/M +Hahn/M +Haida/SM +Haifa/M +Hainan/M +Haiphong/M +Haiti/M +Haitian/MS +Hakka/M +Hakluyt/M +Hal/SM +Haldane/M +Hale/M +Haleakala/M +Haley/M +Halifax/M +Hall/M +Halley/M +Halliburton/M +Hallie/M +Hallmark/M +Halloween/MS +Hallstatt/M +Halon/M +Hals/M +Halsey/M +Ham/M +Haman/M +Hamburg/MS +Hamhung/M +Hamilcar/M +Hamill/M +Hamilton/M +Hamiltonian/M +Hamitic/M +Hamlet/M +Hamlin/M +Hammarskjold/M +Hammerstein/M +Hammett/M +Hammond/M +Hammurabi/M +Hampshire/M +Hampton/M +Hamsun/M +Han/SM +Hancock/M +Handel/M +Handy/M +Haney/M +Hanford/M +Hangul/M +Hangzhou/M +Hank/M +Hanna/M +Hannah/M +Hannibal/M +Hanoi/M +Hanover/M +Hanoverian/M +Hans/MN +Hansel/M +Hansen/M +Hanson/M +Hanuka +Hanukkah/M +Hanukkahs +Hapsburg/M +Harare/M +Harbin/M +Hardin/M +Harding/M +Hardy/M +Hargreaves/M +Harlan/M +Harlem/M +Harlequin/M +Harley/M +Harlingen/M +Harlow/M +Harmon/M +Harold/M +Harper/M +Harpy/SM +Harrell/M +Harriet/M +Harriett/M +Harrington/M +Harris/M +Harrisburg/M +Harrison/M +Harrisonburg/M +Harrods/M +Harry/M +Hart/M +Harte/M +Hartford/M +Hartline/M +Hartman/M +Harvard/M +Harvey/M +Hasbro/M +Hasidim/M +Haskell/M +Hastings/M +Hatfield/M +Hathaway/M +Hatsheput/M +Hatteras/M +Hattie/M +Hattiesburg/M +Hauptmann/M +Hausa/M +Hausdorff/M +Havana/MS +Havarti/M +Havel/M +Havoline/M +Haw +Hawaii/M +Hawaiian/SM +Hawking/M +Hawkins/M +Hawks +Hawthorne/M +Hay/SM +Hayden/M +Haydn/M +Hayek/M +Hayes/M +Haynes/M +Hays/M +Hayward/M +Haywood/M +Hayworth/M +Hazel/M +Hazleton/M +Hazlitt/M +He/M +Head/M +Hearst/M +Heath/MR +Heather/M +Heaviside/M +Heb +Hebe/M +Hebei/M +Hebert/M +Hebraic/M +Hebraism/SM +Hebrew/MS +Hebrews/M +Hebrides/M +Hecate/M +Hector/M +Hecuba/M +Heep/M +Hefner/M +Hegel/M +Hegelian/M +Hegira/M +Heidegger/M +Heidelberg/M +Heidi/M +Heifetz/M +Heilongjiang/M +Heimlich/M +Heine/M +Heineken/M +Heinlein/M +Heinrich/M +Heinz/M +Heisenberg/M +Heisman/M +Helen/M +Helena/M +Helene/M +Helga/M +Helicobacter +Helicon/M +Heliopolis/M +Helios/M +Hellene/SM +Hellenic/M +Hellenism/MS +Hellenist +Hellenistic/M +Hellenization/M +Hellenize/M +Heller/M +Hellespont/M +Hellman/M +Helmholtz/M +Heloise/M +Helsinki/M +Helvetian +Helvetius/M +Hemet/M +Hemingway/M +Henan/M +Hench/M +Henderson/M +Hendrick/MS +Hendricks/M +Hendrix/M +Henley/M +Hennessy/M +Henri/M +Henrietta/M +Henrik/M +Henry/M +Hensley/M +Henson/M +Hepburn/M +Hephaestus/M +Hepplewhite/M +Hera/M +Heracles/M +Heraclitus/M +Herakles/M +Herbart/M +Herbert/M +Herculaneum/M +Herculean +Hercules/M +Herder/M +Hereford/SM +Herero/M +Heriberto/M +Herman/M +Hermaphroditus/M +Hermes/M +Herminia/M +Hermitage/M +Hermite/M +Hermosillo/M +Hernandez/M +Herod/M +Herodotus/M +Heroku/M +Herr/MG +Herrera/M +Herrick/M +Herring/M +Herschel/M +Hersey/M +Hershel/M +Hershey/M +Hertz/M +Hertzsprung/M +Herzegovina/M +Herzl/M +Heshvan/M +Hesiod/M +Hesperia/M +Hesperus/M +Hess/M +Hesse/M +Hessian/M +Hester/M +Heston/M +Hettie/M +Hewitt/M +Hewlett/M +Heyerdahl/M +Heywood/M +Hezbollah/M +Hezekiah/M +Hf/M +Hg/M +Hialeah/M +Hiawatha/M +Hibernia/M +Hibernian +Hickman/M +Hickok/M +Hickory/M +Hicks/M +Hieronymus/M +Higashiosaka +Higgins/M +Highlander/SM +Highlands +Highness/M +Hightstown/M +Hilario/M +Hilary/M +Hilbert/M +Hilda/M +Hildebrand/M +Hilfiger/M +Hill/M +Hillary/M +Hillel/M +Hilton/M +Himalaya/SM +Himalayan +Himalayas/M +Himmler/M +Hinayana/M +Hindemith/M +Hindenburg/M +Hindi/M +Hindu/SM +Hinduism/SM +Hindustan/M +Hindustani/SM +Hines/M +Hinesville/M +Hinton/M +Hipparchus/M +Hippocrates/M +Hippocratic/M +Hiram/M +Hirobumi/M +Hirohito/M +Hiroshima/M +Hispanic/SM +Hispaniola/M +Hiss/M +Hitachi/M +Hitchcock/M +Hitler/MS +Hittite/SM +Hmong/M +Ho/M +Hobart/M +Hobbes/M +Hobbs/M +Hockney/M +Hodge/SM +Hodges/M +Hodgkin/M +Hoff/M +Hoffa/M +Hoffman/M +Hofstadter/M +Hogan/M +Hogarth/M +Hogwarts/M +Hohenlohe/M +Hohenstaufen/M +Hohenzollern/M +Hohhot/M +Hohokam/M +Hokkaido/M +Hokusai/M +Holbein/M +Holcomb/M +Holden/M +Holder/M +Holiday/M +Holiness +Holland/ZSMR +Hollander/M +Hollerith/M +Holley/M +Hollie/M +Hollis/M +Holloway/M +Holly/M +Hollywood/M +Holman/M +Holmes/M +Holocaust/M +Holocene/M +Holst/M +Holstein/SM +Holt/M +Homer/M +Homeric/M +Hon +Honda/M +Honduran/MS +Honduras/M +Honecker/M +Honeywell/M +Hong +Honiara/M +Honolulu/M +Honorable +Honshu/M +Hood/M +Hooke/RM +Hooker/M +Hooper/M +Hoosier/MS +Hooters/M +Hoover/MS +Hope/M +Hopewell/M +Hopi/SM +Hopkins/M +Hopper/M +Horace/M +Horacio/M +Horatio/M +Hormel/M +Hormuz/M +Horn/M +Hornblower/M +Horne/M +Horowitz/M +Horthy/M +Horton/M +Horus/M +Hosea/M +Host/SM +Hotpoint/M +Hottentot/SM +Houdini/M +Houma/M +House/M +Housman/M +Houston/M +Houyhnhnm/M +Hovhaness/M +Howard/M +Howe/M +Howell/MS +Howells/M +Howrah +Hoyle/M +Hrothgar/M +Hts +Huang/M +Hubbard/M +Hubble/M +Hubei/M +Huber/M +Hubert/M +Huck/M +Huddersfield +Hudson/M +Huerta/M +Huey/M +Huff/M +Huffman/M +Huggins/M +Hugh/MS +Hughes/M +Hugo/M +Huguenot/MS +Hui/M +Huitzilopotchli/M +Hull/M +Humberto/M +Humboldt/M +Hume/M +Hummel/M +Hummer/M +Humphrey/SM +Humvee/M +Hun/SM +Hunan/M +Hung/M +Hungarian/SM +Hungary/M +Hunspell/M +Hunt/MR +Hunter/M +Huntington/M +Huntley/M +Huntsville/M +Hurd/M +Hurley/M +Huron/M +Hurst/M +Hus/M +Hussein/M +Husserl/M +Hussite/M +Huston/M +Hutchinson/M +Hutton/M +Hutu/M +Huxley/M +Huygens/M +Hyades/M +Hyde/M +Hyderabad/M +Hydra/M +Hymen/M +Hyperion/M +Hyundai/M +Hz/M +I'd +I'll +I'm +I've +I/M +IA +IBM/M +ICBM/SM +ICC +ICU +ID/SM +IDE +IE +IED +IEEE +IKEA/M +IL +IMF/M +IMHO +IMNSHO +IMO +IN +ING/M +INRI +INS +IOU/M +IP +IPA +IPO +IQ/M +IRA/SM +IRC +IRS/M +ISBN +ISIS +ISO/M +ISP +ISS +IT +IUD +IV/SM +IVF +Ia +Iaccoca/M +Iago/M +Ian/M +Iapetus/M +Ibadan/M +Iberia/M +Iberian/M +Ibiza/M +Iblis/M +Ibo/M +Ibsen/M +Icahn/M +Icarus/M +Ice +Iceland/MRZ +Icelander/M +Icelandic/M +Ida/M +Idaho/SM +Idahoan/MS +Idahoes +Ieyasu/M +Ignacio/M +Ignatius/M +Igor/M +Iguassu/M +Ijsselmeer/M +Ike/M +Ikhnaton/M +Ila/M +Ilene/M +Iliad/SM +Ill +Illinois/M +Illinoisan/MS +Illuminati/M +Ilyushin/M +Imelda/M +Imhotep/M +Imodium/M +Imogene/M +Imus/M +In/M +Ina/M +Inc +Inca/SM +Inchon/M +Incorporated +Ind +Independence/M +India/M +Indian/MS +Indiana/M +Indianan/SM +Indianapolis/M +Indianian +Indies/M +Indio/M +Indira/M +Indochina/M +Indochinese/M +Indonesia/M +Indonesian/SM +Indore/M +Indra/M +Indus/M +Indy/SM +Ines/M +Inez/M +Inge/M +Inglewood +Ingram/M +Ingres/M +Ingrid/M +Innocent/M +Innsbruck +Inonu/M +Inquisition/M +Inst +Instagram/M +Instamatic/M +Intel/M +Intelsat/M +Internationale/M +Internet/SM +Interpol/M +Inuit/MS +Inuktitut/M +Invar/M +Io/M +Ionesco/M +Ionian/MS +Ionic/SM +Iowa/SM +Iowan/MS +Iphigenia/M +Ipswich +Iqaluit/M +Iqbal/M +Iquitos/M +Ir/M +Ira/M +Iran/M +Iranian/SM +Iraq/M +Iraqi/MS +Ireland/M +Irene/M +Iris/M +Irish/MR +Irishman/M +Irishmen/M +Irishwoman/M +Irishwomen/M +Irkutsk/M +Irma/M +Iroquoian/SM +Iroquois/M +Irrawaddy/M +Irtish/M +Irvin/M +Irvine/M +Irving/M +Irwin/M +Isaac/M +Isabel/M +Isabela/M +Isabella/M +Isabelle/M +Isaiah/M +Iscariot/M +Isfahan/M +Isherwood/M +Ishim/M +Ishmael/M +Ishtar/M +Isiah/M +Isidro/M +Isis/M +Islam/MS +Islamabad/M +Islamic/M +Islamism/M +Islamist/M +Islamophobia +Islamophobic +Ismael/M +Ismail/M +Isolde/M +Ispell/M +Israel/SM +Israeli/SM +Israelite/M +Issac/M +Issachar/M +Istanbul/M +Isuzu/M +It +Itaipu/M +Ital +Italian/SM +Italianate +Italy/M +Itasca/M +Ithaca/M +Ithacan/M +Ito/M +Iva/M +Ivan/M +Ivanhoe/M +Ives/M +Ivorian +Ivory/M +Ivy/M +Iyar/M +Izaak/M +Izanagi/M +Izanami/M +Izhevsk/M +Izmir/M +Izod/M +Izvestia/M +J/MD +JCS +JD +JFK/M +JP +JPEG +JV +Jack/M +Jackie/M +Jacklyn/M +Jackson/M +Jacksonian/M +Jacksonville/M +Jacky/M +Jaclyn/M +Jacob/SM +Jacobean/M +Jacobi/M +Jacobin/M +Jacobite/M +Jacobs/M +Jacobson/M +Jacquard/M +Jacqueline/M +Jacquelyn/M +Jacques/M +Jacuzzi/M +Jagger/M +Jagiellon/M +Jaguar/M +Jahangir/M +Jaime/M +Jain/M +Jainism/M +Jaipur/M +Jakarta/M +Jake/M +Jamaal/M +Jamaica/M +Jamaican/SM +Jamal/M +Jamar/M +Jame/SM +Jamel/M +James/M +Jamestown/M +Jami/M +Jamie/M +Jan/M +Jana/M +Janacek/M +Jane/M +Janell/M +Janelle/M +Janesville/M +Janet/M +Janette/M +Janice/M +Janie/M +Janine/M +Janis/M +Janissary/M +Janjaweed/M +Janna/M +Jannie/M +Jansen/M +Jansenist/M +January/SM +Janus/M +Jap/SM +Japan/M +Japanese/MS +Japura/M +Jared/M +Jarlsberg/M +Jarred/M +Jarrett/M +Jarrod/M +Jarvis/M +Jasmine/M +Jason/M +Jasper/M +Jataka/M +Java/SM +JavaScript/M +Javanese/M +Javier/M +Jaxartes/M +Jay/M +Jayapura/M +Jayawardene/M +Jaycee/MS +Jaycees/M +Jayne/M +Jayson/M +Jean/M +Jeanette/M +Jeanie/M +Jeanine/M +Jeanne/M +Jeannette/M +Jeannie/M +Jeannine/M +Jed/M +Jedi/M +Jeep/M +Jeeves/M +Jeff/M +Jefferey/M +Jefferson/M +Jeffersonian/M +Jeffery/M +Jeffrey/M +Jeffry/M +Jehoshaphat/M +Jehovah/M +Jekyll/M +Jenifer/M +Jenkins/M +Jenna/M +Jenner/M +Jennie/M +Jennifer/M +Jennings/M +Jenny/M +Jensen/M +Jephthah/M +Jerald/M +Jeremiah/M +Jeremiahs +Jeremy/M +Jeri/M +Jericho/M +Jermaine/M +Jeroboam/M +Jerold/M +Jerome/M +Jerri/M +Jerrod/M +Jerrold/M +Jerry/M +Jersey/MS +Jerusalem/M +Jess/M +Jesse/M +Jessica/M +Jessie/M +Jesuit/MS +Jesus/M +Jetway/M +Jew/SM +Jewel/M +Jewell/M +Jewess/MS +Jewish/PM +Jewry/M +Jezebel/SM +Jiangsu/M +Jiangxi/M +Jidda/M +Jilin/M +Jill/M +Jillian/M +Jim/M +Jimenez/M +Jimmie/M +Jimmy/M +Jinan/M +Jinnah/M +Jinny/M +Jivaro/M +Jo/M +Joan/M +Joann/M +Joanna/M +Joanne/M +Joaquin/M +Job/SM +Jobs/M +Jocasta/M +Jocelyn/M +Jock/M +Jockey/M +Jodi/M +Jodie/M +Jody/M +Joe/M +Joel/M +Joey/M +Jogjakarta/M +Johann/M +Johanna/M +Johannes/M +Johannesburg/M +John/SM +Johnathan/M +Johnathon/M +Johnie/M +Johnnie/M +Johnny/M +Johns/M +Johnson/M +Johnston/M +Johnstown/M +Jolene/M +Jolson/M +Jon/M +Jonah/M +Jonahs +Jonas/M +Jonathan/M +Jonathon/M +Jones/M +Jonesboro/M +Joni/M +Jonson/M +Joplin/M +Jordan/M +Jordanian/MS +Jorge/M +Jose/M +Josef/M +Josefa/M +Josefina/M +Joseph/M +Josephine/M +Josephs +Josephson/M +Josephus/M +Josh/M +Joshua/M +Josiah/M +Josie/M +Josue/M +Joule/M +Jove/M +Jovian/M +Joy/M +Joyce/M +Joycean/M +Joyner/M +Jpn +Jr/M +Juan/M +Juana/M +Juanita/M +Juarez/M +Jubal/M +Judaeo +Judah/M +Judaic +Judaical +Judaism/MS +Judas/MS +Judd/M +Jude/M +Judea/M +Judges +Judith/M +Judson/M +Judy/M +Juggernaut/M +Jul +Jules/M +Julia/M +Julian/M +Juliana/M +Julianne/M +Julie/M +Juliet/M +Juliette/M +Julio/M +Julius/M +Julliard/M +July/SM +Jun/M +June/SM +Juneau/M +Jung/M +Jungfrau/M +Jungian/M +Junior/SM +Junker/SM +Juno/M +Jupiter/M +Jurassic/M +Jurua/M +Justice/M +Justin/M +Justine/M +Justinian/M +Jutland/M +Juvenal/M +K/SMNGJ +KB/M +KC +KFC/M +KGB/M +KIA +KKK/M +KO/M +KP +KS +KY +Kaaba/M +Kabul/M +Kafka/M +Kafkaesque/M +Kagoshima/M +Kahlua/M +Kahului/M +Kaifeng/M +Kailua/M +Kaiser/MS +Kaitlin/M +Kalahari/M +Kalamazoo/M +Kalashnikov/M +Kalb/M +Kalevala/M +Kalgoorlie/M +Kali/M +Kalmyk/M +Kama/M +Kamchatka/M +Kamehameha/M +Kampala/M +Kampuchea/M +Kan/SM +Kanchenjunga/M +Kandahar/M +Kandinsky/M +Kane/M +Kaneohe/M +Kankakee/M +Kannada/M +Kano/M +Kanpur/M +Kansan/MS +Kansas/M +Kant/M +Kantian/M +Kaohsiung/M +Kaposi/M +Kara/M +Karachi/M +Karaganda/M +Karakorum/M +Karamazov/M +Kareem/M +Karen/M +Karenina/M +Kari/M +Karin/M +Karina/M +Karl/M +Karla/M +Karloff/M +Karo/M +Karol/M +Karroo/M +Karyn/M +Kasai/M +Kasey/M +Kashmir/SM +Kasparov/M +Kate/M +Katelyn/M +Katharine/M +Katherine/M +Katheryn/M +Kathiawar/M +Kathie/M +Kathleen/M +Kathmandu/M +Kathrine/M +Kathryn/M +Kathy/M +Katie/M +Katina/M +Katmai/M +Katowice/M +Katrina/M +Katy/M +Kauai/M +Kaufman/M +Kaunas/M +Kaunda/M +Kawabata/M +Kawasaki/M +Kay/M +Kaye/M +Kayla/M +Kazakh/M +Kazakhs +Kazakhstan/M +Kazan/M +Kazantzakis/M +Kb/M +Keaton/M +Keats/M +Keck/M +Keenan/M +Keewatin/M +Keillor/M +Keisha/M +Keith/M +Keller/M +Kelley/M +Kelli/M +Kellie/M +Kellogg/M +Kelly/M +Kelsey/M +Kelvin/M +Kemerovo/M +Kemp/M +Kempis/M +Ken/M +Kendall/M +Kendra/M +Kendrick/M +Kenmore/M +Kennan/M +Kennedy/M +Kenneth/M +Kennewick/M +Kennith/M +Kenny/M +Kenosha/M +Kent/M +Kenton/M +Kentuckian/MS +Kentucky/M +Kenya/M +Kenyan/SM +Kenyatta/M +Kenyon/M +Keogh/M +Keokuk/M +Kepler/M +Kerensky/M +Keri/M +Kermit/M +Kern/M +Kerouac/M +Kerr/M +Kerri/M +Kerry/M +Kettering/M +Keven/M +Kevin/M +Kevlar/M +Kevorkian/M +Kewpie/M +Key/M +Keynes/M +Keynesian/M +Khabarovsk/M +Khachaturian/M +Khalid/M +Khan/M +Kharkov/M +Khartoum/M +Khayyam/M +Khazar/M +Khmer/M +Khoikhoi/M +Khoisan/M +Khomeini/M +Khorana/M +Khrushchev/M +Khufu/M +Khulna/M +Khwarizmi/M +Khyber/M +Kickapoo/M +Kidd/M +Kiel/M +Kierkegaard/M +Kieth/M +Kiev/M +Kigali/M +Kikuyu/M +Kilauea/M +Kilimanjaro/M +Killeen/M +Kilroy/M +Kim/M +Kimberley/M +Kimberly/M +King/M +Kingsport/M +Kingston/M +Kingstown/M +Kinko's +Kinney/M +Kinsey/M +Kinshasa/M +Kiowa/MS +Kip/M +Kipling/M +Kirby/M +Kirchhoff/M +Kirchner/M +Kirghistan/M +Kirghiz/M +Kirghizia/M +Kiribati/M +Kirinyaga/M +Kirk/M +Kirkland/M +Kirkpatrick/M +Kirov/M +Kirsten/M +Kisangani/M +Kishinev/M +Kislev/M +Kissimmee/M +Kissinger/M +Kit/M +Kitakyushu/M +Kitchener/M +Kitty/M +Kiwanis/M +Klan/M +Klansman/M +Klaus/M +Klee/M +Kleenex/MS +Klein/M +Klimt/M +Kline/M +Klingon/M +Klondike/MS +Kmart/M +Knapp/M +Knesset/M +Kngwarreye/M +Knickerbocker/M +Knievel/M +Knight/M +Knopf/M +Knossos/M +Knowles/M +Knox/M +Knoxville/M +Knudsen/M +Knuth/M +Knuths +Kobe/M +Koch/M +Kochab/M +Kodachrome/M +Kodak/M +Kodaly/M +Kodiak/M +Koestler/M +Kohinoor/M +Kohl/M +Koizumi/M +Kojak/M +Kokomo/M +Kolyma/M +Kommunizma/M +Kong/M +Kongo/M +Konrad/M +Koontz/M +Koppel/M +Korea/M +Korean/SM +Kornberg/M +Kory/M +Korzybski/M +Kosciusko/M +Kossuth/M +Kosygin/M +Kotlin/M +Koufax/M +Kowloon/M +Kr/M +Kraft/M +Krakatoa/M +Krakow/M +Kramer/M +Krasnodar/M +Krasnoyarsk/M +Krebs/M +Kremlin/M +Kremlinologist +Kremlinology +Kresge/M +Kringle/M +Kris/M +Krishna/M +Krishnamurti/M +Krista/M +Kristen/M +Kristi/M +Kristie/M +Kristin/M +Kristina/M +Kristine/M +Kristopher/M +Kristy/M +Kroc/M +Kroger/M +Kronecker/M +Kropotkin/M +Kruger/M +Krugerrand/M +Krupp/M +Krystal/M +Kshatriya/M +Kublai/M +Kubrick/M +Kuhn/M +Kuibyshev/M +Kulthumm/M +Kunming/M +Kuomintang/M +Kurd/M +Kurdish/M +Kurdistan/M +Kurosawa/M +Kurt/M +Kurtis/M +Kusch/M +Kutuzov/M +Kuwait/M +Kuwaiti/SM +Kuznets/M +Kuznetsk/M +Kwakiutl/M +Kwan/M +Kwangju/M +Kwanzaa/MS +Ky/MH +Kyle/M +Kyoto/M +Kyrgyzstan/M +Kyushu/M +L'Amour/M +L'Enfant +L'Oreal/M +L'Ouverture/M +L/MN +LA +LAN/M +LBJ/M +LC +LCD/M +LCM +LDC +LED/M +LG/M +LGBT +LIFO +LL +LLB/M +LLD/M +LNG +LOGO +LP/M +LPG +LPN/SM +LSAT +LSD/M +LVN +La/SM +Lab +Laban/M +Labrador/SM +Labradorean +Lacey/M +Lachesis/M +Lactobacillus +Lacy/M +Ladoga/M +Ladonna/M +Lady/M +Ladyship/MS +Lafayette/M +Lafitte/M +Lagos/M +Lagrange/M +Lagrangian/M +Lahore/M +Laius/M +Lajos/M +Lakeisha/M +Lakeland/M +Lakewood +Lakisha/M +Lakota/M +Lakshmi/M +Lamaism/SM +Lamar/M +Lamarck/M +Lamaze/M +Lamb/M +Lambert/M +Lamborghini/M +Lambrusco/M +Lamentations +Lamont/M +Lana/M +Lanai/M +Lancashire/M +Lancaster/M +Lance/M +Lancelot/M +Land/M +Landon/M +Landry/M +Landsat/M +Landsteiner/M +Lane/M +Lang/M +Langerhans/M +Langland/M +Langley/M +Langmuir/M +Lanka/M +Lankan/M +Lanny/M +Lansing/M +Lanzhou/M +Lao/SM +Laocoon/M +Laos/M +Laotian/SM +Laplace/M +Laplacian +Lapland/MR +Lapp/SM +Lara/M +Laramie/M +Lardner/M +Laredo/M +Larousse/M +Larry/M +Lars/MN +Larsen/M +Larson/M +Lascaux/M +Lassa/M +Lassen/M +Lassie/M +Lat/M +Latasha/M +Lateran/M +Latham/M +Latin/MRS +Latina +Latino/SM +Latinx +Latisha/M +Latonya/M +Latoya/M +Latrobe/M +Latvia/M +Latvian/MS +Laud/MR +Lauder/M +Laue/M +Laundromat/M +Laura/M +Laurasia/M +Laurel/M +Lauren/M +Laurence/M +Laurent/M +Lauri/M +Laurie/M +Laval/M +Lavern/M +Laverne/M +Lavoisier/M +Lavonne/M +Lawanda/M +Lawrence/M +Lawson/M +Lawton/M +Layamon/M +Layla/M +Layton/M +Lazaro/M +Lazarus/M +Le/SM +Lea/M +Leach/M +Leadbelly/M +Leah/M +Leakey/M +Lean/M +Leander/M +Leann/M +Leanna/M +Leanne/M +Lear/M +Learjet/M +Leary/M +Leavenworth/M +Lebanese/M +Lebanon/M +Lebesgue/M +Leblanc/M +Leda/M +Lederberg/M +Lee/M +Leeds/M +Leesburg/M +Leeuwenhoek/M +Leeward/M +Left +Legendre/M +Leger/M +Leghorn/M +Lego/M +Legree/M +Lehman/M +Leibniz/M +Leicester/SM +Leiden/M +Leif/M +Leigh/M +Leila/M +Leipzig/M +Lela/M +Leland/M +Lelia/M +Lemaitre/M +Lemuel/M +Lemuria/M +Len/M +Lena/M +Lenard/M +Lenin/M +Leningrad/M +Leninism/M +Leninist/M +Lennon/M +Lenny/M +Leno/M +Lenoir/M +Lenora/M +Lenore/M +Lent/SMN +Lenten/M +Leo/SM +Leola/M +Leominster/M +Leon/M +Leona/M +Leonard/M +Leonardo/M +Leoncavallo/M +Leonel/M +Leonid/M +Leonidas/M +Leonor/M +Leopold/M +Leopoldo/M +Lepidus/M +Lepke/M +Lepus/M +Lerner/M +Leroy/M +Les/M +Lesa/M +Lesley/M +Leslie/M +Lesotho/M +Lesseps/M +Lessie/M +Lester/M +Lestrade/M +Leta/M +Letha/M +Lethe/M +Leticia/M +Letitia/M +Letterman/M +Levant/M +Levesque/M +Levi/SM +Leviathan/M +Levine/M +Leviticus/M +Levitt/M +Levy/M +Lew/M +Lewinsky/M +Lewis/M +Lewiston/M +Lewisville/M +Lexington/M +Lexus/M +Lhasa/MS +Lhotse/M +Li/MY +Liaoning/M +Libby/M +Liberace/M +Liberal +Liberia/M +Liberian/SM +Libra/MS +LibreOffice/M +Libreville/M +Librium/M +Libya/M +Libyan/SM +Lichtenstein/M +Lidia/M +Lie/M +Lieberman/M +Liebfraumilch/M +Liechtenstein/ZMR +Liechtensteiner/M +Liege/M +Lieut +Lila/M +Lilia/M +Lilian/M +Liliana/M +Lilith/M +Liliuokalani/M +Lille/M +Lillian/M +Lillie/M +Lilliput/M +Lilliputian/MS +Lilly/M +Lilongwe/M +Lily/M +Lima/M +Limbaugh/M +Limbo +Limburger/M +Limoges/M +Limousin/M +Limpopo/M +Lin/M +Lina/M +Lincoln/MS +Lind/M +Linda/M +Lindbergh/M +Lindsay/M +Lindsey/M +Lindy/M +Linnaeus/M +Linotype/M +Linton/M +Linus/M +Linux/MS +Linwood/M +Lionel/M +Lipizzaner/M +Lippi/M +Lippmann/M +Lipscomb/M +Lipton/M +Lisa/M +Lisbon/M +Lissajous/M +Lister/M +Listerine/M +Liston/M +Liszt/M +Lithuania/M +Lithuanian/MS +Little/M +Litton/M +Livermore/M +Liverpool/M +Liverpudlian/SM +Livia/M +Livingston/M +Livingstone/M +Livonia/M +Livy/M +Liz/M +Liza/M +Lizzie/M +Lizzy/M +Ljubljana/M +Llewellyn/M +Lloyd/M +Ln +Loafer/SM +Lobachevsky/M +Lochinvar/M +Locke/M +Lockean/M +Lockheed/M +Lockwood/M +Lodge/M +Lodi/M +Lodz/M +Loewe/M +Loewi/M +Loews/M +Logan/M +Lohengrin/M +Loire/M +Lois/M +Loki/M +Lola/M +Lolita/M +Lollard/M +Lollobrigida/M +Lombard/M +Lombardi/M +Lombardy/M +Lome/M +Lompoc/M +Lon/M +London/MRZ +Londoner/M +Long/M +Longfellow/M +Longmont/M +Longstreet/M +Longueuil +Longview/M +Lonnie/M +Lopez/M +Lora/M +Lorain/M +Loraine/M +Lord/SM +Lordship/SM +Lorelei/M +Loren/M +Lorena/M +Lorene/M +Lorentz/M +Lorentzian +Lorenz/M +Lorenzo/M +Loretta/M +Lori/M +Lorie/M +Lorna/M +Lorraine/M +Lorre/M +Lorrie/M +Los +Lot/M +Lothario/SM +Lott/M +Lottie/M +Lou/M +Louella/M +Louie/M +Louis/M +Louisa/M +Louise/M +Louisiana/M +Louisianan/MS +Louisianian/MS +Louisville/M +Lourdes/M +Louvre/M +Love/M +Lovecraft/M +Lovelace/M +Lowe/M +Lowell/M +Lowenbrau/M +Lowery/M +Lowlands +Loyang/M +Loyd/M +Loyola/M +Lr +Lt +Ltd +Lu/M +Luanda/M +Luann/M +Lubavitcher/M +Lubbock/M +Lubumbashi/M +Lucas/M +Luce/M +Lucia/M +Lucian/M +Luciano/M +Lucien/M +Lucifer/M +Lucile/M +Lucille/M +Lucinda/M +Lucio/M +Lucite/SM +Lucius/M +Lucknow/M +Lucretia/M +Lucretius/M +Lucy/M +Luddite/MS +Ludhiana/M +Ludwig/M +Luella/M +Lufthansa/M +Luftwaffe/M +Luger/M +Lugosi/M +Luigi/M +Luis/M +Luisa/M +Luke/M +Lula/M +Lully/M +Lulu/M +Lumiere/M +Luna/M +Lupe/M +Lupercalia/M +Lupus/M +Luria/M +Lusaka/M +Lusitania/M +Luther/M +Lutheran/SM +Lutheranism/MS +Luvs/M +Luxembourg/ZMR +Luxembourger/M +Luxembourgian +Luz/M +Luzon/M +Lvov/M +LyX/M +Lyallpur +Lycra/M +Lycurgus/M +Lydia/M +Lydian/SM +Lyell/M +Lyle/M +Lyly/M +Lyman/M +Lyme/M +Lynch/M +Lynchburg/M +Lynda/M +Lyndon/M +Lynette/M +Lynn/M +Lynne/M +Lynnette/M +Lyon/SM +Lyons/M +Lyra/M +Lysenko/M +Lysistrata/M +Lysol/M +M/SMGB +MA/M +MASH +MB/M +MBA/M +MC +MCI/M +MD/M +MDT +ME +MEGO/S +MFA/M +MGM/M +MHz +MI/M +MIA +MIDI/M +MIPS +MIRV +MIT/M +MM +MN +MO +MOOC +MP/M +MPEG +MRI/M +MS/M +MSG/M +MST/M +MSW +MT/M +MTV/M +MVP/M +MW +Maalox/M +Mabel/M +Mable/M +Mac/M +MacArthur/M +MacBride/M +MacDonald/M +MacLeish/M +Macao/M +Macaulay/M +Macbeth/M +Maccabees +Maccabeus/M +Mace/M +Macedon/M +Macedonia/M +Macedonian/SM +Mach/M +Machiavelli/M +Machiavellian/M +Macias/M +Macintosh/M +Mack/M +Mackenzie/M +Mackinac/M +Mackinaw/M +Macmillan/M +Macon/M +Macumba/M +Macy/M +Madagascan/SM +Madagascar/M +Madam +Madden/M +Maddox/M +Madeira/SM +Madeleine/M +Madeline/M +Madelyn/M +Madera/M +Madge/M +Madison/M +Madonna/SM +Madras/M +Madrid/M +Madurai/M +Mae/M +Maeterlinck/M +Mafia/MS +Mafioso/M +Magdalena/M +Magdalene/M +Magellan/M +Magellanic/M +Maggie/M +Maghreb/M +Magi +Maginot/M +Magnificat +Magnitogorsk/M +Magog/M +Magoo/M +Magritte/M +Magsaysay/M +Magus +Magyar/SM +Mahabharata/M +Maharashtra/M +Mahavira/M +Mahayana/M +Mahayanist/M +Mahdi/M +Mahfouz/M +Mahican/SM +Mahler/M +Mai/M +Maidenform/M +Maigret/M +Mailer/M +Maillol/M +Maiman/M +Maimonides/M +Maine/MZR +Mainer/M +Maisie/M +Maitreya/M +Maj +Majesty +Major/M +Majorca/M +Majuro/M +Makarios/M +Maker/M +Malabar/M +Malabo/M +Malacca/M +Malachi/M +Malagasy/M +Malamud/M +Malaprop/M +Malawi/M +Malawian/SM +Malay/MS +Malaya/M +Malayalam/M +Malayan/MS +Malaysia/M +Malaysian/MS +Malcolm/M +Maldive/MS +Maldives/M +Maldivian/MS +Maldonado/M +Male/M +Mali/M +Malian/SM +Malibu/M +Malinda/M +Malinowski/M +Mallarme/M +Mallomars/M +Mallory/M +Malone/M +Malory/M +Malplaquet/M +Malraux/M +Malta/M +Maltese/M +Malthus/M +Malthusian/SM +Mameluke/M +Mamet/M +Mamie/M +Mamore/M +Man/M +Managua/M +Manama/M +Manasseh/M +Manchester/M +Manchu/SM +Manchuria/M +Manchurian/M +Mancini/M +Mancunian/MS +Mandalay/M +Mandarin/M +Mandela/M +Mandelbrot/M +Mandeville/M +Mandingo/M +Mandrell/M +Mandy/M +Manet/M +Manfred/M +Manhattan/SM +Mani/M +Manichean/M +Manila/SM +Manitoba/M +Manitoulin/M +Mankato/M +Manley/M +Mann/GM +Mannheim/M +Manning/M +Mansfield/M +Manson/M +Manteca/M +Mantegna/M +Mantle/M +Manuel/M +Manuela/M +Manx/M +Mao/M +Maoism/SM +Maoist/SM +Maori/MS +Mapplethorpe/M +Maputo/M +Mar/SM +Mara/M +Maracaibo/M +Marat/M +Maratha/M +Marathi/M +Marathon/M +Marc/M +Marceau/M +Marcel/M +Marcelino/M +Marcella/M +Marcelo/M +March/MS +Marci/M +Marcia/M +Marciano/M +Marcie/M +Marco/MS +Marconi/M +Marcos/M +Marcus/M +Marcuse +Marcy/M +Marduk/M +Margaret/M +Margarita/M +Margarito/M +Marge/M +Margery/M +Margie/M +Margo/M +Margot +Margret/M +Margrethe/M +Marguerite/M +Mari/SM +Maria/M +MariaDB/M +Marian/M +Mariana/SM +Marianas/M +Marianne/M +Mariano/M +Maribel/M +Maricela/M +Marie/M +Marietta/M +Marilyn/M +Marin/M +Marina/M +Marine/SM +Mario/M +Marion/M +Maris/M +Marisa/M +Marisol/M +Marissa/M +Maritain/M +Maritza/M +Mariupol +Marius/M +Marjorie/M +Marjory/M +Mark/SM +Markab/M +Markham/M +Markov/M +Marks/M +Marla/M +Marlboro/M +Marlborough/M +Marlene/M +Marley/M +Marlin/M +Marlon/M +Marlowe/M +Marmara/M +Marne/M +Maronite/M +Marple/M +Marquesas/M +Marquette/M +Marquez/M +Marquis/M +Marquita/M +Marrakesh/M +Marriott/M +Mars/MS +Marsala/M +Marseillaise/MS +Marseilles/M +Marsh/M +Marsha/M +Marshall/M +Marta/M +Martel/M +Martha/M +Martial/M +Martian/SM +Martin/M +Martina/M +Martinez/M +Martinique/M +Marty/M +Marva/M +Marvell/M +Marvin/M +Marx/M +Marxian +Marxism/SM +Marxist/SM +Mary/M +Maryann/M +Maryanne/M +Maryellen/M +Maryland/MR +Marylander/M +Marylou/M +Marysville/M +Masada/M +Masai/M +Masaryk/M +Mascagni/M +Masefield/M +Maserati/M +Maseru/M +Mashhad/M +Mason/MS +Masonic/M +Masonite/M +Mass/MS +Massachusetts/M +Massasoit/M +Massenet/M +Massey/M +Master/S +MasterCard/M +Masters/M +Mather/M +Matheson/M +Mathew/SM +Mathews/M +Mathewson/M +Mathias/M +Mathis/M +Matilda/M +Matisse/M +Matlab/M +Matt/M +Mattel/M +Matterhorn/M +Matthew/SM +Matthews/M +Matthias/M +Mattie/M +Maud/M +Maude/M +Maugham/M +Maui/M +Mauldin/M +Maupassant/M +Maura/M +Maureen/M +Mauriac/M +Maurice/M +Mauricio/M +Maurine/M +Mauritania/M +Mauritanian/SM +Mauritian/SM +Mauritius/M +Mauro/M +Maurois/M +Mauryan/M +Mauser/M +Mavis/M +Max/M +Maximilian/M +Maxine/M +Maxwell/M +May/SMR +Maya/SM +Mayan/MS +Mayer/M +Mayfair/M +Mayflower/M +Maynard/M +Mayo/M +Maypole +Mayra/M +Mays/M +Maytag/M +Mazama/M +Mazarin/M +Mazatlan/M +Mazda/M +Mazola/M +Mazzini/M +Mb/M +Mbabane/M +Mbini/M +McAdam/M +McAllen/M +McBride/M +McCain/M +McCall/M +McCarthy/M +McCarthyism/M +McCartney/M +McCarty/M +McClain/M +McClellan/M +McClure/M +McConnell/M +McCormick/M +McCoy/M +McCray/M +McCullough/M +McDaniel/M +McDonald/M +McDonnell/M +McDowell/M +McEnroe/M +McFadden/M +McFarland/M +McGee/M +McGovern/M +McGowan/M +McGuffey/M +McGuire/M +McHenry/M +McIntosh/M +McIntyre/M +McJob +McKay/M +McKee/M +McKenzie/M +McKinley/M +McKinney/M +McKnight/M +McLaughlin/M +McLean/M +McLeod/M +McLuhan/M +McMahon/M +McMillan/M +McNamara/M +McNaughton/M +McNeil/M +McPherson/M +McQueen/M +McVeigh/M +Md/M +Me +Mead/M +Meade/M +Meadows/M +Meagan/M +Meany/M +Mecca/MS +Medan/M +Medea/M +Medellin/M +Medford/M +Media/M +Medicaid/SM +Medicare/SM +Medici/M +Medina/M +Mediterranean/MS +Medusa/M +Meg/M +Megan/M +Meghan/M +Meier/M +Meighen/M +Meiji/M +Meir/M +Mejia/M +Mekong/M +Mel/M +Melanesia/M +Melanesian/M +Melanie/M +Melba/M +Melbourne/M +Melchior/M +Melchizedek/M +Melendez/M +Melinda/M +Melisa/M +Melisande/M +Melissa/M +Mellon/M +Melody/M +Melpomene/M +Melton/M +Melva/M +Melville/M +Melvin/M +Memcached/M +Memling/M +Memphis/M +Menander/M +Mencius/M +Mencken/M +Mendel/M +Mendeleev/M +Mendelian/M +Mendelssohn/M +Mendez/M +Mendocino/M +Mendoza/M +Menelaus/M +Menelik/M +Menes/M +Mengzi +Menifee/M +Menkalinan/M +Menkar/M +Menkent/M +Mennen/M +Mennonite/MS +Menominee/M +Menotti/M +Mensa/M +Mentholatum/M +Menuhin/M +Menzies/M +Mephisto +Mephistopheles/M +Merak/M +Mercado/M +Mercator/M +Merced/M +Mercedes/M +Mercer/M +Mercia/M +Merck/M +Mercurochrome/M +Mercury/SM +Meredith/M +Merino/M +Merle/M +Merlin/M +Merlot/M +Merovingian/M +Merriam/M +Merrick/M +Merrill/M +Merrimack/M +Merritt/M +Merthiolate/M +Merton/M +Mervin/M +Mesa/M +Mesabi/M +Mesmer/M +Mesolithic/M +Mesopotamia/M +Mesopotamian +Mesozoic/M +Messerschmidt/M +Messiaen/M +Messiah/M +Messiahs +Messianic +Messieurs +Metallica/M +Metamucil/M +Methodism/SM +Methodist/SM +Methuselah/M +Metternich/M +Meuse/M +Mex +Mexicali/M +Mexican/MS +Mexico/M +Meyer/MS +Meyerbeer/M +Meyers/M +Mfume/M +Mg/M +Mgr +MiG/M +Mia/M +Miami/MS +Miaplacidus/M +Micah/M +Micawber/M +Mich/M +Michael/M +Michaelmas/MS +Micheal/M +Michel/M +Michelangelo/M +Michele/M +Michelin/M +Michelle/M +Michelob/M +Michelson/M +Michigan/M +Michigander/MS +Michiganite +Mick/M +Mickey/M +Mickie/M +Micky/M +Micmac/SM +Micronesia/M +Micronesian/M +Microsoft/M +Midas/M +Middleton/M +Middletown/M +Mideast +Mideastern +Midland/MS +Midway/M +Midwest/M +Midwestern/MR +Miguel/M +Mike/M +Mikhail/M +Mikoyan/M +Milagros/M +Milan/M +Milanese +Mildred/M +Miles/M +Milford/M +Milken/M +Mill/SMR +Millard/M +Millay/M +Miller/M +Millet/M +Millicent/M +Millie/M +Millikan/M +Mills/M +Milne/M +Milo/M +Milosevic/M +Milquetoast/M +Miltiades/M +Milton/M +Miltonian +Miltonic/M +Miltown/M +Milwaukee/M +Mimi/M +Mimosa/M +Min/M +Minamoto/M +Mindanao/M +Mindoro/M +Mindy/M +Minerva/M +Ming/M +Mingus/M +Minn +Minneapolis/M +Minnelli/M +Minnesota/M +Minnesotan/SM +Minnie/M +Minoan/MS +Minolta/M +Minos/M +Minot/M +Minotaur/M +Minsk/M +Minsky/M +Mintaka/M +Minuit/M +Minuteman/M +Miocene/M +Mir/M +Mira/M +Mirabeau/M +Mirach/M +Miranda/M +Mirfak/M +Miriam/M +Miro/M +Mirzam/M +Miskito/M +Miss +Mississauga/M +Mississippi/M +Mississippian/SM +Missoula/M +Missouri/M +Missourian/MS +Missy/M +Mistassini/M +Mister +Mistress +Misty/M +Mitch/M +Mitchel/M +Mitchell/M +Mitford/M +Mithra/M +Mithridates/M +Mitsubishi/M +Mitterrand/M +Mitty/M +Mitzi/M +Mixtec/M +Mizar/M +Mk +Mlle +Mme/S +Mn/M +Mnemosyne/M +Mo/M +Mobil/M +Mobile/M +Mobutu/M +Modesto/M +Modigliani/M +Moe/M +Moet/M +Mogadishu/M +Mogul/MS +Mohacs/M +Mohamed/M +Mohammad/M +Mohammedan/SM +Mohammedanism/SM +Mohave/SM +Mohawk/SM +Mohegan +Moho/M +Mohorovicic/M +Moira/M +Moises/M +Moiseyev/M +Mojave/SM +Moldavia/M +Moldavian +Moldova/M +Moldovan +Moliere/M +Molina/M +Moll/M +Mollie/M +Molly/M +Molnar/M +Moloch/M +Molokai/M +Molotov/M +Moluccas/M +Mombasa/M +Mon/SM +Mona/M +Monacan +Monaco/M +Mondale/M +Monday/SM +Mondrian/M +Monegasque/SM +Monera/M +Monessen/M +Monet/M +MongoDB/M +Mongol/SM +Mongolia/M +Mongolian/SM +Mongolic/M +Mongoloid +Monica/M +Monique/M +Monk/M +Monmouth/M +Monongahela/M +Monroe/M +Monrovia/M +Monsanto/M +Monsieur/M +Monsignor/SM +Mont/M +Montague/M +Montaigne/M +Montana/M +Montanan/SM +Montcalm/M +Monte/M +Montenegrin/M +Montenegro/M +Monterey/M +Monterrey/M +Montesquieu/M +Montessori/M +Monteverdi/M +Montevideo/M +Montezuma/M +Montgolfier/M +Montgomery/M +Monticello/M +Montoya/M +Montpelier/M +Montrachet/M +Montreal/M +Montserrat/M +Monty/M +Moody/M +Moog/M +Moon/M +Mooney/M +Moor/SM +Moore/M +Moorish/M +Morales/M +Moran/M +Moravia/M +Moravian/M +Mordred/M +More/M +Moreno/M +Morgan/SM +Morgantown/M +Moriarty/M +Morin/M +Morison/M +Morita/M +Morley/M +Mormon/SM +Mormonism/SM +Moro/M +Moroccan/SM +Morocco/M +Moroni/M +Morpheus/M +Morphy/M +Morris/M +Morrison/M +Morristown/M +Morrow/M +Morse/M +Mort/M +Mortimer/M +Morton/M +Mosaic/M +Moscow/M +Moseley/M +Moselle/M +Moses/M +Mosley/M +Moss/M +Mosul/M +Motorola/M +Motown/M +Motrin/M +Mott/M +Moulton/M +Mount/M +Mountbatten/M +Mountie/MS +Moussorgsky/M +Mouthe/M +Mouton/M +Mowgli/M +Mozambican/SM +Mozambique/M +Mozart/M +Mozilla/M +Mr/SM +Ms/S +Msgr +Mt +Muawiya/M +Mubarak/M +Mueller/M +Muenster/MS +Mugabe/M +Muhammad/M +Muhammadan/MS +Muhammadanism/SM +Muir/M +Mujib/M +Mulder/M +Mullen/M +Muller/M +Mulligan/M +Mullikan/M +Mullins/M +Mulroney/M +Multan/M +Multics +Mumbai/M +Mumford/M +Munch/M +Munchhausen/M +Muncie/M +Munich/M +Munoz/M +Munro/M +Munster/M +Muppet/M +Murasaki/M +Murat/M +Murchison/M +Murcia +Murdoch/M +Murfreesboro/M +Muriel/M +Murillo/M +Murine/M +Murmansk/M +Murphy/M +Murray/M +Murrieta/M +Murrow/M +Murrumbidgee/M +Muscat/M +Muscovite/M +Muscovy/M +Muse/M +Musharraf/M +Musial/M +Muskegon/M +Muskogee/M +Muslim/MS +Mussolini/M +Mussorgsky/M +Mutsuhito/M +Muzak/M +MySQL/M +MySpace/M +Myanmar/M +Mycenae/M +Mycenaean/M +Myers/M +Mylar/MS +Myles/M +Myra/M +Myrdal/M +Myrna/M +Myron/M +Myrtle/M +Mysore/M +Myst/M +N'Djamena +N/MD +NAACP/M +NAFTA/M +NASA/M +NASCAR/M +NASDAQ/M +NATO/M +NB +NBA/M +NBC/M +NBS +NC +NCAA/M +NCO +ND +NE/M +NEH +NF +NFC +NFL/M +NH +NHL/M +NIH +NIMBY +NJ +NLRB +NM +NORAD/M +NOW +NP +NPR/M +NR +NRA +NRC +NS +NSA/M +NSC +NSF +NSFW +NT +NV +NVIDIA/M +NW/M +NWT +NY +NYC +NYSE +NZ +Na/M +Nabisco/M +Nabokov/M +Nader/M +Nadia/M +Nadine/M +Nagasaki/M +Nagoya/M +Nagpur/M +Nagy/M +Nahuatl/MS +Nahum/M +Naipaul/M +Nair/M +Nairobi/M +Naismith/M +Nam/M +Namath/M +Namibia/M +Namibian/MS +Nampa/M +Nan/M +Nanak/M +Nanchang/M +Nancy/M +Nanette/M +Nanjing/M +Nannie/M +Nanook/M +Nansen/M +Nantes/M +Nantucket/M +Naomi/M +Napa/M +Naphtali/M +Napier/M +Naples/M +Napoleon/MS +Napoleonic/M +Napster/M +Narcissus/M +Narmada/M +Narnia/M +Narraganset +Narragansett/M +Nash/M +Nashua/M +Nashville/M +Nassau/M +Nasser/M +Nat/M +Natalia/M +Natalie/M +Natasha/M +Natchez/M +Nate/MN +Nathan/SM +Nathaniel/M +Nathans/M +Nation/M +Nationwide/M +Nativity/M +Naugahyde/M +Nauru/M +Nautilus/M +Navajo/SM +Navajoes +Navarre/M +Navarro/M +Navratilova/M +Navy +Nazarene/M +Nazareth/M +Nazca/M +Nazi/SM +Nazism/MS +Nb/M +Nd/M +Ndjamena/M +Ne/M +NeWS +NeWSes +Neal/M +Neanderthal/SM +Neapolitan/M +Neb +Nebr +Nebraska/M +Nebraskan/MS +Nebuchadnezzar/M +Ned/M +Nefertiti/M +Negev/M +Negress/MS +Negritude +Negro/MS +Negroes +Negroid/SM +Negros/M +Nehemiah/M +Nehru/M +Neil/M +Nelda/M +Nell/M +Nellie/M +Nelly/M +Nelsen/M +Nelson/M +Nembutal/M +Nemesis/M +Neo/M +Neogene/M +Neolithic +Nepal/M +Nepalese/M +Nepali/MS +Neptune/M +Nereid/M +Nerf/M +Nero/M +Neruda/M +Nescafe/M +Nesselrode/M +Nestle/M +Nestor/M +Nestorius/M +Netflix/M +Netherlander/SM +Netherlands/M +Netscape/M +Nettie/M +Netzahualcoyotl/M +Nev/M +Neva/M +Nevada/M +Nevadan/SM +Nevadian +Nevis/M +Nevsky/M +Newark/M +Newburgh/M +Newcastle/M +Newfoundland/MRS +Newman/M +Newport/M +Newsweek/M +Newton/M +Newtonian/M +Nexis/M +Ngaliema/M +Nguyen/M +Ni/M +Niagara/M +Niamey/M +Nibelung/M +Nicaea/M +Nicaragua/M +Nicaraguan/SM +Niccolo/M +Nice/M +Nicene/M +Nichiren/M +Nicholas/M +Nichole/M +Nichols/M +Nicholson/M +Nick/M +Nickelodeon/M +Nicklaus/M +Nickolas/M +Nicobar/M +Nicodemus/M +Nicola/SM +Nicolas/M +Nicole/M +Nicosia/M +Niebuhr/M +Nielsen/M +Nietzsche/M +Nieves/M +Nigel/M +Niger/M +Nigeria/M +Nigerian/MS +Nigerien/M +Nightingale/M +Nijinsky/M +Nike/M +Nikita/M +Nikkei/M +Nikki/M +Nikolai/M +Nikon/M +Nile/M +Nimitz/M +Nimrod/M +Nina/M +Nineveh/M +Nintendo/M +Niobe/M +Nippon/M +Nipponese/M +Nirenberg/M +Nirvana/M +Nisan/M +Nisei/M +Nissan/M +Nita/M +Nivea/M +Nixon/M +Nkrumah/M +No/SM +NoDoz/M +Noah/M +Nobel/M +Nobelist/MS +Noble/M +Noe/M +Noel/SM +Noelle/M +Noemi/M +Nokia/M +Nola/M +Nolan/M +Nome/M +Nona/M +Nootka/M +Nora/M +Norbert/M +Norberto/M +Nordic/MS +Noreen/M +Norfolk/M +Noriega/M +Norma/M +Normal/M +Norman/MS +Normand/M +Normandy/M +Norplant/M +Norris/M +Norse/M +Norseman/M +Norsemen/M +North/M +Northampton/M +Northeast/MS +Northerner/M +Northrop/M +Northrup/M +Norths +Northwest/SM +Norton/M +Norw +Norway/M +Norwegian/SM +Norwich/M +Nosferatu/M +Nostradamus/M +Nottingham/M +Nouakchott/M +Noumea/M +Nov/M +Nova/M +Novartis/M +November/MS +Novgorod/M +Novocain/MS +Novocaine +Novokuznetsk/M +Novosibirsk/M +Noxzema/M +Noyce/M +Noyes/M +Np/M +Nubia/M +Nubian/M +Nukualofa/M +Numbers/M +Nunavut/M +Nunez/M +Nunki/M +Nuremberg/M +Nureyev/M +NutraSweet/M +NyQuil/M +Nyasa/M +Nyerere/M +O'Brien/M +O'Casey/M +O'Connell/M +O'Connor/M +O'Donnell/M +O'Hara/M +O'Higgins/M +O'Keeffe/M +O'Neil/M +O'Neill/M +O'Rourke/M +O'Toole/M +O/SM +OAS/M +OB +OCR +OD/SM +OE +OED +OH +OHSA/M +OJ +OK/SMDG +OMB/M +ON +OPEC/M +OR +OS/M +OSHA/M +OSes +OT +OTB +OTC +OTOH +Oahu/M +Oakland/M +Oakley/M +Oates/M +Oaxaca/M +Ob/M +Obadiah/M +Obama/M +Obamacare +Oberlin/M +Oberon/M +Ocala/M +Ocaml/M +Occam/M +Occident +Occidental/MS +Oceania/M +Oceanside +Oceanus/M +Ochoa/M +Oct/M +Octavia/M +Octavian/M +Octavio/M +October/SM +Odell/M +Oder/M +Odessa/M +Odets/M +Odin/M +Odis/M +Odom/M +Odysseus/M +Odyssey/M +Oedipal/M +Oedipus/M +Oersted/M +Ofelia/M +Offenbach/M +OfficeMax/M +Ogbomosho/M +Ogden/M +Ogilvy/M +Oglethorpe/M +Ohio/M +Ohioan/SM +Oise/M +Ojibwa/SM +Okayama +Okeechobee/M +Okefenokee/M +Okhotsk/M +Okinawa/M +Okinawan +Okla +Oklahoma/M +Oklahoman/M +Oktoberfest/M +Ola/M +Olaf/M +Olajuwon/M +Olav/M +Oldenburg/M +Oldfield/M +Oldsmobile/M +Olduvai/M +Olen/M +Olenek/M +Olga/M +Oligocene/M +Olin/M +Olive/MR +Oliver/M +Olivetti/M +Olivia/M +Olivier/M +Ollie/M +Olmec/M +Olmsted/M +Olsen/M +Olson/M +Olympia/SM +Olympiad/MS +Olympian/MS +Olympic/SM +Olympics/M +Olympus/M +Omaha/MS +Oman/M +Omani/MS +Omar/M +Omayyad/M +Omdurman/M +Omnipotent +Omsk/M +Onassis/M +Oneal/M +Onega/M +Onegin/M +Oneida/MS +Onion/M +Ono/M +Onondaga/MS +Onsager/M +Ont +Ontarian +Ontario/M +Oort/M +Opal/M +Opel/M +OpenOffice/M +Ophelia/M +Ophiuchus/M +Oppenheimer/M +Opposition +Oprah/M +Ora/M +Oracle/M +Oran/M +Orange/M +Oranjestad/M +Orbison/M +Ordovician/M +Ore/N +Oreg +Oregon/M +Oregonian/SM +Orem/M +Oreo/M +Orestes/M +Orient/M +Oriental/MS +Orientalism +Orin/M +Orinoco/M +Orion/M +Oriya/M +Orizaba/M +Orkney/M +Orlando/M +Orleans/M +Orlon/MS +Orly/M +Orpheus/M +Orphic/M +Orr/M +Ortega/M +Orthodox +Ortiz/M +Orval/M +Orville/M +Orwell/M +Orwellian/M +Os/M +Osage/MS +Osaka/M +Osbert/M +Osborn/M +Osborne/M +Oscar/MS +Osceola/M +Osgood/M +Oshawa/M +Oshkosh/M +Osiris/M +Oslo/M +Osman/M +Ostrogoth/M +Ostwald/M +Osvaldo/M +Oswald/M +Othello/M +Otis/M +Ottawa/SM +Otto/M +Ottoman/M +Ouagadougou/M +Ouija/MS +Ovid/M +Owen/SM +Owens/M +Owensboro/M +Oxford/SM +Oxnard/M +Oxonian/M +Oxus/M +Oxycontin/M +Oz/M +Ozark/MS +Ozarks/M +Ozymandias/M +Ozzie/M +P/MN +PA/M +PAC/M +PARC/S +PASCAL +PBS/M +PBX +PC/SM +PCB +PCMCIA +PCP/M +PD +PDF +PDQ +PDT +PE +PET/M +PFC +PG +PGP +PHP/M +PIN +PJ's +PLO/M +PM/SMDG +PMS/M +PO +POW/M +PP +PPS +PR +PRC/M +PRO +PS/M +PST/M +PT +PTA/M +PTO +PVC/M +PW +PX +Pa/M +Paar/M +Pablo/M +Pablum/M +Pabst/M +Pace/M +Pacheco/M +Pacific/M +Pacino/M +Packard/M +Padang +Paderewski/M +Padilla/M +Paganini/M +Page/M +Paglia/M +Pahlavi/M +Paige/M +Paine/M +Paiute/SM +Pakistan/M +Pakistani/SM +Palembang/M +Paleocene/M +Paleogene/M +Paleolithic/M +Paleozoic/M +Palermo/M +Palestine/M +Palestinian/SM +Palestrina/M +Paley/M +Palikir/M +Palisades/M +Palladio/M +Palmdale/M +Palmer/M +Palmerston/M +Palmolive/M +Palmyra/M +Palomar/M +Pam/M +Pamela/M +Pamirs/M +Pampers/M +Pan/M +Panama/SM +Panamanian/MS +Panasonic/M +Pandora/M +Pangaea/M +Pankhurst/M +Panmunjom/M +Pansy/M +Pantagruel/M +Pantaloon/M +Pantheon/M +Panza/M +Paracelsus/M +Paraclete/M +Paradise +Paraguay/M +Paraguayan/MS +Paralympic/S +Paramaribo/M +Paramount/M +Parana/M +Parcheesi/M +Pareto/M +Paris/M +Parisian/MS +Park/SMR +Parker/M +Parkersburg/M +Parkinson/M +Parkinsonism +Parkman/M +Parks/M +Parliament/M +Parmenides +Parmesan/MS +Parnassus/MS +Parnell/M +Parr/M +Parrish/M +Parsifal/M +Parsons/M +Parthenon/M +Parthia/M +Pasadena/M +Pascagoula/M +Pascal/SM +Pasco/M +Pasquale/M +Passion/SM +Passover/MS +Pasternak/M +Pasteur/M +Pat/M +Patagonia/M +Patagonian/M +Pate/M +Patel/M +Paterson/M +Patna/M +Patrica/M +Patrice/M +Patricia/M +Patrick/M +Patsy/M +Patterson/M +Patti/M +Patton/M +Patty/M +Paul/GM +Paula/M +Paulette/M +Pauli/M +Pauline/M +Pauling/M +Pavarotti/M +Pavlov/M +Pavlova/M +Pavlovian/M +Pawnee/SM +PayPal/M +Payne/M +Pb/M +Pd/M +Peabody/M +Peace/M +Peale/M +Pearl/M +Pearlie/M +Pearson/M +Peary/M +Pechora/M +Peck/M +Peckinpah/M +Pecos/M +Pedro/M +Peel/M +Peg/M +Pegasus/MS +Peggy/M +Pei/M +Peiping/M +Peking/SM +Pekingese/SM +Pele/M +Pelee/M +Peloponnese/M +Pembroke/M +Pen/M +Pena/M +Penderecki/M +Penelope/M +Penn/M +Penna +Penney/M +Pennington/M +Pennsylvania/M +Pennsylvanian/MS +Penny/M +Pennzoil/M +Pensacola/M +Pentagon/M +Pentateuch/M +Pentax/M +Pentecost/SM +Pentecostal/MS +Pentecostalism +Pentium/SM +Peoria/M +Pepin/M +Pepsi/M +Pepys/M +Pequot/M +Percheron/M +Percival/M +Percy/M +Perelman/M +Perez/M +Periclean/M +Pericles/M +Perkins/M +Perl/SM +Perm/M +Permalloy/M +Permian/M +Pernod/M +Peron/M +Perot/M +Perrier/M +Perry/RM +Perseid/M +Persephone/M +Persepolis/M +Perseus/M +Pershing/M +Persia/M +Persian/SM +Perth/M +Peru/M +Peruvian/MS +Peshawar/M +Petain/M +Petaluma/M +Pete/RMZ +Peter/M +Peters/MN +Petersen/M +Peterson/M +Petra/M +Petrarch/M +Petty/M +Peugeot/M +Pfc +Pfizer/M +PhD/M +Phaedra/M +Phaethon/M +Phanerozoic/M +Pharaoh/M +Pharaohs +Pharisaic +Pharisaical +Pharisee/MS +Phekda/M +Phelps/M +Phidias/M +Phil/MY +Philadelphia/M +Philby/M +Philemon/M +Philip/MS +Philippe/M +Philippians/M +Philippine/SM +Philippines/M +Philips/M +Philistine/M +Phillip/SM +Phillipa/M +Phillips/M +Philly/M +Phipps/M +Phobos/M +Phoebe/M +Phoenicia/M +Phoenician/SM +Phoenix/M +Photostat/MS +Photostatted +Photostatting +Phrygia/M +Phyllis/M +Piaf/M +Piaget/M +Pianola/M +Picasso/M +Piccadilly/M +Pickering/M +Pickett/M +Pickford/M +Pickwick/M +Pict/M +Piedmont/M +Pierce/M +Pierre/M +Pierrot/M +Pike/M +Pilate/MS +Pilates/M +Pilcomayo/M +Pilgrim/SM +Pillsbury/M +Pinatubo/M +Pincus/M +Pindar/M +Pinkerton/M +Pinocchio/M +Pinochet/M +Pinter/M +Pinyin +Pippin/M +Piraeus/M +Pirandello/M +Pisa/M +Pisces/M +Pisistratus/M +Pissaro/M +Pitcairn/M +Pitt/SM +Pittman/M +Pitts/M +Pittsburgh/M +Pittsfield/M +Pius/M +Pizarro/M +Pkwy +Pl +Planck/M +Plano +Plantagenet/M +Plasticine/M +Plataea/M +Plath/M +Plato/M +Platonic +Platonism/M +Platonist/M +Platte/M +Plautus/M +PlayStation/M +Playboy/M +Playtex/M +Pleiades/M +Pleistocene/M +Plexiglas/MS +Pliny/M +Pliocene/SM +Plutarch/M +Pluto/M +Plymouth/M +Pm/M +Po/M +Pocahontas/M +Pocatello/M +Pocono/SM +Poconos/M +Podgorica/M +Podhoretz/M +Podunk/M +Poe/M +Pogo/M +Poincare/M +Poiret/M +Poirot/M +Poisson/M +Poitier/M +Pokemon/M +Pol/MY +Poland/M +Polanski/M +Polaris/M +Polaroid/MS +Pole/SM +Polish/M +Politburo/M +Polk/M +Pollard/M +Pollock/M +Pollux/M +Polly/M +Pollyanna/M +Polo/M +Poltava/M +Polyhymnia/M +Polynesia/M +Polynesian/MS +Polyphemus/M +Pomerania/M +Pomeranian/M +Pomona/M +Pompadour/M +Pompeian +Pompeii/M +Pompey/M +Ponce/M +Pontchartrain/M +Pontiac/M +Pontianak/M +Pooh/M +Poole/M +Poona/M +Pope/M +Popeye/M +Popocatepetl/M +Popper/M +Poppins/M +Popsicle/M +Porfirio/M +Porrima/M +Porsche/M +Port/MR +Porter/M +Porterville/M +Portia/M +Portland/M +Porto/M +Portsmouth/M +Portugal/M +Portuguese/M +Poseidon/M +Post/M +PostgreSQL/M +Potemkin/M +Potomac/M +Potsdam/M +Pottawatomie/M +Potter/M +Potts/M +Pottstown/M +Poughkeepsie/M +Pound/M +Poussin/M +Powell/M +PowerPC/M +PowerPoint/M +Powers/M +Powhatan/M +Poznan/M +Pr/M +Prada/M +Prado/M +Praetorian/M +Prague/M +Praia/M +Prakrit/M +Pratchett/M +Pratt/M +Pravda/M +Praxiteles/M +Preakness/M +Precambrian/M +Preminger/M +Premyslid/M +Prensa/M +Prentice/M +Pres +Presbyterian/SM +Presbyterianism/MS +Prescott/M +Presley/M +Preston/M +Pretoria/M +Priam/M +Pribilof/M +Price/M +Priceline/M +Priestley/M +Prince/M +Princeton/M +Principe/M +Priscilla/M +Prius/M +Private +Procrustean/M +Procrustes/M +Procter/M +Procyon/M +Prof +Prohibition +Prokofiev/M +Promethean/M +Prometheus/M +Prophets +Proserpina/M +Proserpine/M +Protagoras/M +Proterozoic/M +Protestant/MS +Protestantism/SM +Proteus/M +Proudhon/M +Proust/M +Provencal/MS +Provence/M +Proverbs +Providence/SM +Provo/M +Prozac/MS +Prudence/M +Prudential/M +Pruitt/M +Prussia/M +Prussian/MS +Prut/M +Pryor/M +Psalms/M +Psalter/MS +Psyche/M +Pt/M +Ptah/M +Ptolemaic/M +Ptolemy/SM +Pu/M +Puccini/M +Puck/M +Puckett/M +Puebla/M +Pueblo/M +Puerto +Puget/M +Pugh/M +Pulaski/M +Pulitzer/M +Pullman/MS +Punch/M +Punic/M +Punjab/M +Punjabi/M +Purana/M +Purcell/M +Purdue/M +Purim/MS +Purina/M +Puritan/M +Puritanism/MS +Purus/M +Pusan/M +Pusey/M +Pushkin/M +Pushtu/M +Putin/M +Putnam/M +Puzo/M +Pvt +PyTorch/M +Pygmalion/M +Pygmy/SM +Pyle/M +Pym/M +Pynchon/M +Pyongyang/M +Pyotr/M +Pyrenees/M +Pyrex/MS +Pyrrhic/M +Pythagoras/M +Pythagorean/M +Pythias/M +Python/M +Q +QA +QB +QC +QED +QM +QWERTY +Qaddafi/M +Qantas/M +Qatar/M +Qatari/MS +Qingdao/M +Qinghai/M +Qiqihar/M +Qom/M +Quaalude/M +Quaker/MS +Quakerism/SM +Qualcomm/M +Quaoar/M +Quasimodo/M +Quaternary/M +Quayle/M +Que +Quebec/M +Quebecois/M +Quechua/M +Queen/MS +Queens/M +Queensland/M +Quentin/M +Quetzalcoatl/M +Quezon/M +Quincy/M +Quinn/M +Quintilian/M +Quinton/M +Quirinal/M +Quisling/M +Quito/M +Quixote/M +Quixotism/M +Qumran/M +Quonset/M +Quran +Quranic +R/M +RAF/M +RAM/SM +RBI +RC +RCA/M +RCMP +RD +RDA +RDS/M +REIT +REM/SM +RF +RFC/S +RFD +RI +RIF +RIP +RISC +RN/M +RNA/M +ROFL +ROM/M +ROTC/M +RP +RR +RSFSR +RSI +RSV +RSVP +RTFM +RV/SM +Ra/M +Rabat/M +Rabelais/M +Rabelaisian/M +Rabin/M +Rachael/M +Rachel/M +Rachelle/M +Rachmaninoff/M +Racine/M +Radcliff/M +Radcliffe/M +Rae/M +Rafael/M +Raffles/M +Ragnarok/M +Rainier/M +Raleigh/M +Ralph/M +Rama/M +Ramada/M +Ramadan/MS +Ramakrishna/M +Ramanujan/M +Ramayana/M +Rambo/M +Ramirez/M +Ramiro/M +Ramon/M +Ramona/M +Ramos/M +Ramsay/M +Ramses/M +Ramsey/M +Rand/M +Randal/M +Randall/M +Randell/M +Randi/M +Randolph/M +Randy/M +Rangoon/M +Rankin/M +Rankine/M +Raoul/M +Raphael/M +Rappaport/M +Rapunzel/M +Raquel/M +Rasalgethi/M +Rasalhague/M +Rasmussen/M +Rasputin/M +Rasta +Rastaban/M +Rastafarian/MS +Rastafarianism +Rather/M +Ratliff/M +Raul/M +Ravel/M +Rawalpindi/M +Ray/M +RayBan/M +Rayburn/M +Rayleigh/M +Raymond/M +Raymundo/M +Rb/M +Rd +Re/M +Reading/M +Reagan/M +Reaganomics/M +Realtor/M +Reasoner/M +Reba/M +Rebekah/M +Recife/M +Reconstruction/M +Redding/M +Redeemer/M +Redford/M +Redgrave/M +Redis/M +Redmond/M +Redshift/M +Reebok/M +Reed/M +Reese/M +Reeves/M +Reformation/MS +Refugio/M +Reggie/M +Regina/M +Reginae/M +Reginald/M +Regor/M +Regulus/M +Rehnquist/M +Reich/M +Reichstag's +Reid/M +Reilly/M +Reinaldo/M +Reinhardt/M +Reinhold/M +Remarque/M +Rembrandt/M +Remington/M +Remus/M +Rena/M +Renaissance/SM +Renascence +Renault/M +Rene/M +Renee/M +Reno/M +Renoir/M +Rep +Representative +Republican/SM +Republicanism +Requiem/MS +Resistance +Restoration/M +Resurrection +Reuben/M +Reunion/M +Reuters/M +Reuther/M +Rev +Reva/M +Revelation/SM +Revelations/M +Revere/M +Reverend/M +Revlon/M +Rex/M +Reyes/M +Reykjavik/M +Reyna/M +Reynaldo/M +Reynolds/M +Rf/M +Rh/M +Rhea/M +Rhee/M +Rheingau/M +Rhenish/M +Rhiannon/M +Rhine/M +Rhineland/M +Rhoda/M +Rhode/S +Rhodes/M +Rhodesia/M +Rhodesian +Rhonda/M +Rhone/M +Ribbentrop/M +Ricardo/M +Rice/M +Rich/M +Richard/MS +Richards/M +Richardson/M +Richelieu/M +Richie/M +Richmond/M +Richter/M +Richthofen/M +Rick/M +Rickenbacker/M +Rickey/M +Rickie/M +Rickover/M +Ricky/M +Rico/M +Riddle/M +Ride/M +Riefenstahl/M +Riel/M +Riemann/M +Riesling/MS +Riga/M +Rigel/M +Riggs/M +Right +Rigoberto/M +Rigoletto/M +Riley/M +Rilke/M +Rimbaud/M +Ringling/M +Ringo/M +Rio/SM +Rios/M +Ripley/M +Risorgimento/M +Rita/M +Ritalin/M +Ritz/M +Rivas/M +Rivera/M +Rivers/M +Riverside/M +Riviera/MS +Riyadh/M +Rizal/M +Rn/M +Roach/M +Roanoke/M +Rob/M +Robbie/M +Robbin/MS +Robbins/M +Robby/M +Roberson/M +Robert/MS +Roberta/M +Roberto/M +Roberts/M +Robertson/M +Robeson/M +Robespierre/M +Robin/M +Robinson/M +Robitussin/M +Robles/M +Robson/M +Robt/M +Robyn/M +Rocco/M +Rocha/M +Rochambeau/M +Roche/M +Rochelle/M +Rochester/M +Rock/M +Rockefeller/M +Rockford/M +Rockies/M +Rockne/M +Rockwell/M +Rocky/SM +Rod/M +Roddenberry/M +Roderick/M +Rodger/MS +Rodgers/M +Rodin/M +Rodney/M +Rodolfo/M +Rodrick/M +Rodrigo/M +Rodriguez/M +Rodriquez/M +Roeg/M +Roentgen +Rogelio/M +Roger/MS +Rogers/M +Roget/M +Rojas/M +Roku/M +Rolaids/M +Roland/M +Rolando/M +Rolex/M +Rolland/M +Rollerblade/M +Rollins/M +Rolodex/M +Rolvaag/M +Rom +Roman/MS +Romanesque/MS +Romania/M +Romanian/MS +Romano/M +Romanov/M +Romans/M +Romansh/M +Romanticism +Romany/SM +Rome/SM +Romeo/M +Romero/M +Rommel/M +Romney/M +Romulus/M +Ron/M +Ronald/M +Ronda/M +Ronnie/M +Ronny/M +Ronstadt/M +Rontgen +Rooney/M +Roosevelt/M +Root/M +Roquefort/SM +Rorschach/M +Rory/M +Rosa/M +Rosales/M +Rosalie/M +Rosalind/M +Rosalinda/M +Rosalyn/M +Rosanna/M +Rosanne/M +Rosario/M +Roscoe/M +Rose/M +Roseann/M +Roseau/M +Rosecrans/M +Rosella/M +Rosemarie/M +Rosemary/M +Rosenberg/M +Rosendo/M +Rosenzweig/M +Rosetta/M +Rosicrucian/M +Rosie/M +Roslyn/M +Ross/M +Rossetti/M +Rossini/M +Rostand/M +Rostov/M +Rostropovich/M +Roswell/M +Rotarian/M +Roth/M +Rothko/M +Rothschild/M +Rotterdam/M +Rottweiler/M +Rouault/M +Rourke/M +Rousseau/M +Rove/RM +Rover/M +Rowe/M +Rowena/M +Rowland/M +Rowling/M +Roxanne/M +Roxie/M +Roxy/M +Roy/M +Royal/M +Royce/M +Rozelle/M +Rte +Ru/MH +Rubaiyat/M +Rubbermaid/M +Ruben/SM +Rubens/M +Rubicon/MS +Rubik/M +Rubin/M +Rubinstein/M +Ruby/M +Ruchbah/M +Rudolf/M +Rudolph/M +Rudy/M +Rudyard/M +Rufus/M +Ruhr/M +Ruiz/M +Rukeyser/M +Rumpelstiltskin/M +Rumsfeld/M +Runnymede/M +Runyon/M +Rupert/M +Rush/M +Rushdie/M +Rushmore/M +Ruskin/M +Russ/M +Russel/M +Russell/M +Russia/M +Russian/SM +Russo/M +Rustbelt/M +Rusty/M +Rutan/M +Rutgers/M +Ruth/M +Rutherford/M +Ruthie/M +Rutledge/M +Rwanda/MS +Rwandan/SM +Rwy +Rx +Ry +Ryan/M +Rydberg/M +Ryder/M +Ryukyu/M +S/MN +SA +SAC +SALT/M +SAM/M +SAP/M +SARS/M +SASE +SAT +SBA +SC/M +SCSI/M +SD +SDI +SE/M +SEATO +SEC/M +SF +SGML/M +SIDS/M +SJ +SJW +SK +SLR +SO/S +SOB/M +SOP/M +SOS/M +SOSes +SPCA +SPF +SQL +SQLite/M +SRO +SS +SSA +SSE/M +SSS +SST +SSW/M +ST +STD +STOL +SUSE/M +SUV +SVN/M +SW/M +SWAK +SWAT +Saab/M +Saar/M +Saarinen/M +Saatchi/M +Sabbath/M +Sabbaths +Sabik/M +Sabin/M +Sabina/M +Sabine/M +Sabre/M +Sabrina/M +Sacajawea/M +Sacco/M +Sachs/M +Sacramento/M +Sadat/M +Saddam/M +Sadducee/M +Sade/M +Sadie/M +Sadr/M +Safavid/M +Safeway/M +Sagan/M +Saginaw/M +Sagittarius/MS +Sahara/M +Saharan/M +Sahel/M +Saigon/M +Saiph/M +Sakai/M +Sakha/M +Sakhalin/M +Sakharov/M +Saki/M +Saks/M +Sal/MY +Saladin/M +Salado/M +Salamis/M +Salas/M +Salazar/M +Salem/M +Salerno/M +Salesforce/M +Salinas/M +Salinger/M +Salisbury/M +Salish/M +Salk/M +Sallie/M +Sallust/M +Sally/M +Salome/M +Salonika/M +Salton/M +Salvador/M +Salvadoran/SM +Salvadorean/MS +Salvadorian/MS +Salvatore/M +Salween/M +Salyut/M +Sam/M +Samantha/M +Samar/M +Samara/M +Samaritan/MS +Samarkand/M +Sammie/M +Sammy/M +Samoa/M +Samoan/SM +Samoset/M +Samoyed/M +Sampson/M +Samson/M +Samsonite/M +Samsung/M +Samuel/M +Samuelson/M +San'a +San/M +Sana/M +Sanchez/M +Sancho/M +Sand/ZM +Sandburg/M +Sanders/M +Sandinista/M +Sandoval/M +Sandra/M +Sandy/M +Sanford/M +Sanforized/M +Sang/MR +Sanger/M +Sanhedrin/M +Sanka/M +Sankara/M +Sanskrit/M +Santa/M +Santana/M +Santayana/M +Santeria/M +Santiago/M +Santos/M +Sappho/M +Sapporo/M +Sara/M +Saracen/MS +Saragossa/M +Sarah/M +Sarajevo/M +Saran/M +Sarasota/M +Saratov/M +Sarawak/M +Sardinia/M +Sargasso/M +Sargent/M +Sargon/M +Sarnoff/M +Saroyan/M +Sarto/M +Sartre/M +Sasha/M +Sask +Saskatchewan/M +Saskatoon/M +Sasquatch/MS +Sassanian/M +Sassoon/M +Sat/M +Satan/M +Satanism/M +Satanist/M +Saturday/MS +Saturn/M +Saturnalia/M +Saudi/MS +Saul/M +Saunders/M +Saundra/M +Saussure/M +Sauternes +Savage/M +Savannah/M +Savior/M +Savonarola/M +Savoy/M +Savoyard/M +Sawyer/M +Saxon/MS +Saxony/M +Sayers/M +Sb/M +Sc/M +Scala/M +Scan +Scandinavia/M +Scandinavian/MS +Scaramouch/M +Scarborough/M +Scarlatti/M +Scheat/M +Schedar/M +Scheherazade/M +Schelling/M +Schenectady/M +Schiaparelli/M +Schick/M +Schiller/M +Schindler/M +Schlesinger/M +Schliemann/M +Schlitz/M +Schloss/M +Schmidt/M +Schnabel/M +Schnauzer/M +Schneider/M +Schoenberg/M +Schopenhauer/M +Schrieffer/M +Schrodinger/M +Schroeder/M +Schubert/M +Schultz/M +Schulz/M +Schumann/M +Schumpeter/M +Schuyler/M +Schuylkill/M +Schwartz/M +Schwarzenegger/M +Schwarzkopf/M +Schweitzer/M +Schweppes/M +Schwinger/M +Schwinn/M +Scientologist/SM +Scientology/M +Scipio/M +Scopes/M +Scorpio/SM +Scorpius/M +Scorsese/M +Scot/SM +Scotch/MS +Scotchman/M +Scotchmen/M +Scotchwoman/M +Scotchwomen/M +Scotia/M +Scotland/M +Scotsman/M +Scotsmen/M +Scotswoman/M +Scotswomen/M +Scott/M +Scottie/SM +Scottish/M +Scottsdale/M +Scrabble/MS +Scranton/M +Scriabin/M +Scribner/M +Scripture/SM +Scrooge/M +Scruggs/M +Scud/M +Sculley/M +Scylla/M +Scythia/M +Scythian/M +Se/MH +Seaborg/M +Seagram/M +Sean/M +Sears/M +Seaside/M +Seattle/M +Sebastian/M +Sebring/M +Sec +Seconal/M +Secretariat/M +Secretary +Seder/MS +Sedna/M +Seebeck/M +Seeger/M +Sega/M +Segovia/M +Segre/M +Segundo/M +Segway/S +Seiko/M +Seine/M +Seinfeld/M +Sejong/M +Selassie/M +Selectric/M +Selena/M +Seleucid/M +Seleucus/M +Selim/M +Seljuk/M +Selkirk/M +Sellers/M +Selma/M +Selznick/M +Semarang/M +Seminole/MS +Semiramis/M +Semite/MS +Semitic/SM +Semtex/M +Senate/MS +Sendai/M +Seneca/MS +Senegal/M +Senegalese/M +Senghor/M +Senior/M +Sennacherib/M +Sennett/M +Sensurround/M +Seoul/M +Sep +Sephardi/M +Sepoy/M +Sept/M +September/MS +Septuagint/MS +Sequoya/M +Serb/SM +Serbia/M +Serbian/MS +Serena/M +Serengeti/M +Sergei/M +Sergio/M +Serpens/M +Serra/M +Serrano/M +Set/M +Seth/M +Seton/M +Seurat/M +Seuss/M +Sevastopol/M +Severn/M +Severus/M +Seville/M +Sevres/M +Seward/M +Sextans/M +Sexton/M +Seychelles/M +Seyfert/M +Seymour/M +Sgt +Shaanxi/M +Shackleton/M +Shaffer/M +Shah/M +Shaka/M +Shaker +Shakespeare/M +Shakespearean/M +Shana/M +Shandong/M +Shane/M +Shanghai/M +Shankara/M +Shanna/M +Shannon/M +Shantung/M +Shanxi/M +Shapiro/M +SharePoint/M +Shari'a/M +Shari/M +Sharif/M +Sharlene/M +Sharon/M +Sharp/M +Sharpe/M +Sharron/M +Shasta/M +Shaula/M +Shaun/M +Shauna/M +Shavian/M +Shavuot/M +Shaw/M +Shawn/M +Shawna/M +Shawnee/SM +Shcharansky/M +Shea/M +Sheba/M +Shebeli/M +Sheboygan/M +Sheena/M +Sheetrock/M +Sheffield/M +Sheila/M +Shelby/M +Sheldon/M +Shelia/M +Shell/M +Shelley/M +Shelly/M +Shelton/M +Shenandoah/M +Shenyang/M +Sheol/M +Shepard/M +Shepherd/M +Sheppard/M +Sheratan/M +Sheraton/M +Sheree/M +Sheri/M +Sheridan/M +Sherlock/M +Sherman/M +Sherpa/M +Sherri/M +Sherrie/M +Sherry/M +Sherwood/M +Sheryl/M +Shetland/SM +Shetlands/M +Shevardnadze/M +Shevat/M +Shi'ite/M +Shields/M +Shiite/MS +Shijiazhuang/M +Shikoku/M +Shillong/M +Shiloh/M +Shinto/MS +Shintoism/MS +Shintoist/MS +Shiraz/M +Shirley/M +Shiva/M +Shockley/M +Short/M +Shorthorn/M +Shoshone/SM +Shostakovitch/M +Shrek/M +Shreveport/M +Shriner/M +Shropshire/M +Shula/M +Shylock/M +Shylockian/M +Si/M +Siam/M +Siamese/M +Sibelius/M +Siberia/M +Siberian/MS +Sibyl/M +Sichuan/M +Sicilian/SM +Sicily/M +Sid/M +Siddhartha/M +Sidney/M +Siegfried/M +Siemens/M +Sierpinski/M +Sierras +Sigismund/M +Sigmund/M +Sigurd/M +Sihanouk/M +Sikh/M +Sikhism +Sikhs +Sikkim/M +Sikkimese/M +Sikorsky/M +Silas/M +Silesia/M +Silurian/SM +Silva/M +Silvia/M +Simenon/M +Simmental/M +Simmons/M +Simon/M +Simone/M +Simpson/SM +Simpsons/M +Simpsonville/M +Sims/M +Sinai/M +Sinatra/M +Sinbad/M +Sinclair/M +Sindbad/M +Sindhi/M +Singapore/M +Singaporean/SM +Singer/M +Singh/M +Singleton/M +Sinhalese/M +Sinkiang/M +Sioux/M +Sir/SM +Sirius/M +Sistine/M +Sisyphean/M +Sisyphus/M +Siva/M +Sivan/M +Sjaelland/M +Skinner/M +Skippy/M +Skopje/M +Skye/M +Skylab/M +Skype/M +Slackware/M +Slashdot/M +Slater/M +Slav/SM +Slavic/M +Slavonic/M +Slidell/M +Slinky/M +Sloan/M +Sloane/M +Slocum/M +Slovak/SM +Slovakia/M +Slovakian +Slovene/SM +Slovenia/M +Slovenian/MS +Slurpee/M +Sm/M +Small/M +Smetana/M +Smirnoff/M +Smith/M +Smithson/M +Smithsonian/M +Smokey/M +Smolensk/M +Smollett/M +Smuts/M +Smyrna +Sn/M +Snake/M +Snapple/M +Snead/M +Snell/M +Snickers/M +Snider/M +Snoopy/M +Snow/M +Snowbelt/M +Snyder/M +Soave/M +Soc +Socastee/M +Socorro/M +Socrates/M +Socratic/M +Soddy/M +Sodom/M +Sofia/M +Soho/M +Sol/M +Solis/M +Solomon/M +Solon/M +Solzhenitsyn/M +Somali/SM +Somalia/M +Somalian/MS +Somme/M +Somoza/M +Son/M +Sondheim/M +Sondra/M +Songhai/M +Songhua/M +Sonia/M +Sonja/M +Sonny/M +Sonora/M +Sontag/M +Sony/M +Sonya/M +Sophia/M +Sophie/M +Sophoclean/M +Sophocles/M +Sopwith/M +Sorbonne/M +Sosa/M +Soto/M +Souphanouvong/M +Sourceforge/M +Sousa/M +South/M +Southampton/M +Southeast/MS +Southerner/SM +Southey/M +Souths +Southwest/MS +Soviet/M +Soweto/M +Soyinka/M +Soyuz/M +Sp +Spaatz/M +Spackle/M +Spahn/M +Spain/M +Spam/M +Span +Spanglish +Spaniard/SM +Spanish/M +Sparks/M +Sparta/M +Spartacus/M +Spartan/MS +Spartanburg/M +Spears/M +Speer/M +Spence/RM +Spencer/M +Spencerian/M +Spengler/M +Spenglerian/M +Spenser/M +Spenserian/M +Sperry/M +Sphinx/M +Spica/M +Spielberg/M +Spillane/M +Spinoza/M +Spinx/M +Spiro/M +Spirograph/M +Spitsbergen/M +Spitz/M +Spock/M +Spokane/M +Springdale/M +Springfield/M +Springsteen/M +Sprint/M +Sprite/M +Sputnik/M +Sq +Squanto/M +Squibb/M +Sr/M +Srinagar/M +Srivijaya/M +St +Sta +Stacey/M +Staci/M +Stacie/M +Stacy/M +Stael/M +Stafford/M +StairMaster/M +Stalin/M +Stalingrad/M +Stalinist/M +Stallone/M +Stamford/M +Stan/M +Standish/M +Stanford/M +Stanislavsky/M +Stanley/M +Stanton/M +Staples/M +Starbucks/M +Stark/M +Starkey/M +Starr/M +Staten/M +States +Staubach/M +Staunton/M +Ste +Steadicam/M +Steele/M +Stefan/M +Stefanie/M +Stein/MR +Steinbeck/M +Steinem/M +Steiner/M +Steinmetz/M +Steinway/M +Stella/M +Stendhal/M +Stengel/M +Stephan/M +Stephanie/M +Stephen/MS +Stephens/M +Stephenson/M +Sterling/M +Stern/M +Sterne/M +Sterno/M +Stetson/M +Steuben/M +Steubenville/M +Steve/M +Steven/MS +Stevens/M +Stevenson/M +Stevie/M +Stewart/M +Stieglitz/M +Stilton/SM +Stimson/M +Stine/M +Stirling/M +Stockhausen/M +Stockholm/M +Stockton/M +Stoic/SM +Stoicism/MS +Stokes/M +Stolichnaya/M +Stolypin/M +Stone/M +Stonehenge/M +Stoppard/M +Stout/M +Stowe/M +Strabo/M +Stradivari +Stradivarius/M +Strasbourg/M +Strauss/M +Stravinsky/M +Streisand/M +Strickland/M +Strindberg/M +Stromboli/M +Strong/M +Stu/M +Stuart/MS +Studebaker/M +Stuttgart/M +Stuyvesant/M +Stygian/M +Styrofoam/SM +Styron/M +Styx/M +Suarez/M +Subaru/M +Sucre/M +Sucrets/M +Sudan/M +Sudanese/M +Sudetenland/M +Sudoku/M +Sudra/M +Sue/M +Suetonius/M +Suez/M +Suffolk/M +Sufi/M +Sufism/M +Suharto/M +Sui/M +Sukarno/M +Sukkot +Sulawesi/M +Suleiman/M +Sulla/M +Sullivan/M +Sumatra/M +Sumatran/SM +Sumeria/M +Sumerian/SM +Summer/MS +Summers/M +Sumner/M +Sumter/M +Sun/SM +Sunbeam/M +Sunbelt/M +Sundanese/M +Sundas/M +Sunday/MS +Sung/M +Sunkist/M +Sunni/SM +Sunnite/MS +Sunnyvale/M +Superbowl/M +Superfund/M +Superglue/M +Superior/M +Superman/M +Supt +Surabaya/M +Surat/M +Suriname/M +Surinamese +Surya/M +Susan/M +Susana/M +Susanna/M +Susanne/M +Susie/M +Susquehanna/M +Sussex/M +Sutherland/M +Sutton/M +Suva/M +Suwanee/M +Suzanne/M +Suzette/M +Suzhou/M +Suzuki/M +Suzy/M +Svalbard/M +Sven/M +Svengali/M +Sverdlovsk +Swahili/SM +Swammerdam/M +Swanee/M +Swansea/M +Swanson/M +Swazi/SM +Swaziland/M +Swed/N +Swede/SM +Sweden/M +Swedenborg/M +Swedish/M +Sweeney/M +Sweet/M +Swift/M +Swinburne/M +Swiss/MS +Swissair/M +Switz +Switzerland/M +Sybil/M +Sydney/M +Sykes/M +Sylvester/M +Sylvia/M +Sylvie/M +Synge/M +Syracuse/M +Syria/M +Syriac/M +Syrian/MS +Szilard/M +Szymborska/M +T'ang/M +T/MDG +TA +TARP +TB/M +TBA +TD +TDD +TEFL +TELNET/S +TELNETTed +TELNETTing +TESL +TESOL +TGIF +THC +TKO/M +TLC/M +TM +TN +TNT/M +TOEFL +TQM +TV/SM +TVA +TWA/M +TWX +TX +Ta/M +Tabasco/SM +Tabatha/M +Tabernacle/MS +Tabitha/M +Tabriz/MS +Tacitus/M +Tacoma/M +Tad/M +Tadzhik/M +Taegu/M +Taejon/M +Taft/M +Tagalog/SM +Tagore/M +Tagus/M +Tahiti/M +Tahitian/MS +Tahoe/M +Taichung/M +Tainan +Taine/M +Taipei/M +Taiping/M +Taiwan/M +Taiwanese/M +Taiyuan/M +Tajikistan/M +Taklamakan/M +Talbot/M +Taliban/M +Taliesin/M +Tallahassee/M +Tallchief/M +Talley/M +Talleyrand/M +Tallinn/M +Talmud/MS +Talmudic +Talmudist +Tamara/M +Tameka/M +Tamera/M +Tamerlane/M +Tami/M +Tamika/M +Tamil/MS +Tammany/M +Tammi/M +Tammie/M +Tammuz/M +Tammy/M +Tampa/M +Tampax/M +Tamra/M +Tamworth/M +Tancred/M +Taney/M +Tanganyika/M +Tangier/MS +Tangshan/M +Tania/M +Tanisha/M +Tanner/M +Tannhauser/M +Tantalus/M +Tanya/M +Tanzania/M +Tanzanian/SM +Tao/M +Taoism/MS +Taoist/MS +Tara/M +Tarantino/M +Tarawa/M +Tarazed/M +Tarbell/M +Target/M +Tarim/M +Tarkenton/M +Tarkington/M +Tartary/M +Tartuffe/M +Tarzan/M +Tasha/M +Tashkent/M +Tasman/M +Tasmania/M +Tasmanian/M +Tass/M +Tatar/MS +Tate/M +Tatum/M +Taurus/MS +Tavares/M +Tawney/M +Taylor/M +Tb/M +Tbilisi/M +Tc/M +Tchaikovsky/M +Te/M +TeX +TeXes +Teasdale/M +Technicolor/M +Tecumseh/M +Ted/M +Teddy/M +Teflon/MS +Tegucigalpa/M +Tehran +TelePrompTer +TelePrompter/M +Telemachus/M +Telemann/M +Teletype +Tell/MR +Teller/M +Telugu/M +Temecula/M +Tempe +Templar/M +Temple/M +Tenn/M +Tennessean/SM +Tennessee/M +Tennyson/M +Tennysonian +Tenochtitlan/M +TensorFlow/M +Teotihuacan/M +Terence/M +Teresa/M +Tereshkova/M +Teri/M +Terkel/M +Terpsichore/M +Terr/M +Terra/M +Terran/M +Terrance/M +Terrell/M +Terrence/M +Terri/M +Terrie/M +Terry/M +Tertiary/M +Tesla/M +Tess/M +Tessa/M +Tessie/M +Tet/M +Tethys/M +Tetons/M +Teuton/MS +Teutonic/M +Tevet/M +Tex/M +Texaco/M +Texan/MS +Texarkana/M +Texas/M +Th/M +Thackeray/M +Thad/M +Thaddeus/M +Thai/SM +Thailand/M +Thales/M +Thalia/M +Thames/M +Thanh/M +Thanksgiving/MS +Thant/M +Thar/M +Tharp/M +Thatcher/M +Thea/M +Thebes/M +Theiler/M +Thelma/M +Themistocles/M +Theocritus/M +Theodora/M +Theodore/M +Theodoric/M +Theodosius/M +Theosophy/M +Theravada/M +Theresa/M +Therese/M +Thermopylae/M +Thermos +Theron/M +Theseus/M +Thespian/M +Thespis/M +Thessalonian/SM +Thessaloniki/M +Thessaly/M +Thieu/M +Thimbu/M +Thimphu +Thomas/M +Thomism/M +Thomistic/M +Thompson/M +Thomson/M +Thor/M +Thorazine/M +Thoreau/M +Thornton/M +Thoroughbred/M +Thorpe/M +Thoth/M +Thrace/M +Thracian/M +Thu +Thucydides/M +Thule/M +Thunderbird/M +Thur/S +Thurber/M +Thurman/M +Thurmond/M +Thursday/SM +Thutmose/M +Ti/M +Tia/M +Tianjin/M +Tiber/M +Tiberius/M +Tibet/M +Tibetan/MS +Ticketmaster/M +Ticonderoga/M +Tide/M +Tienanmen/M +Tiffany/M +Tigris/M +Tijuana/M +Tillich/M +Tillman/M +Tilsit/M +Tim/M +Timbuktu/M +Timex/M +Timmy/M +Timon/M +Timor/M +Timothy/M +Timur/M +Timurid/M +Tina/M +Ting/M +Tinkerbell/M +Tinkertoy/M +Tinseltown/M +Tintoretto/M +Tippecanoe/M +Tipperary/M +Tirane +Tiresias/M +Tirol/M +Tirolean +Tisha/M +Tishri/M +Titan/SM +Titania/M +Titanic/M +Titian/M +Titicaca/M +Tito/M +Titus/M +Titusville/M +Tl/M +Tlaloc/M +Tlingit/M +Tm/M +Tobago/M +Tobit/M +Toby/M +Tocantins/M +Tocqueville/M +Tod/M +Todd/M +Togo/M +Togolese/M +Tojo/M +Tokay/M +Tokugawa/M +Tokyo/M +Tokyoite +Toledo/MS +Tolkien/M +Tolstoy/M +Toltec/M +Tolyatti/M +Tom/M +Tomas/M +Tombaugh/M +Tomlin/M +Tommie/M +Tommy/M +Tompkins/M +Tomsk/M +Tonga/M +Tongan/MS +Toni/M +Tonia/M +Tonto/M +Tony/M +Tonya/M +Topeka/M +Topsy/M +Torah/M +Torahs +Toronto/M +Torquemada/M +Torrance/M +Torrens/M +Torres/M +Torricelli/M +Tortola/M +Tortuga/M +Torvalds/M +Tory/SM +Tosca/M +Toscanini/M +Toshiba/M +Toto/M +Toulouse/M +Townes/M +Townsend/M +Toynbee/M +Toyoda/M +Toyota/M +Tracey/M +Traci/M +Tracie/M +Tracy/M +Trafalgar/M +Trailways/M +Trajan/M +Tran/M +Transcaucasia/M +Transvaal/M +Transylvania/M +Transylvanian/M +Trappist/SM +Travis/M +Travolta/M +Treasury/SM +Treblinka/M +Trekkie/M +Trent/M +Trenton/M +Trevelyan/M +Trevino/M +Trevor/M +Trey/M +Triangulum/M +Triassic/M +Tricia/M +Trident/M +Trieste/M +Trimurti/M +Trina/M +Trinidad/M +Trinidadian/MS +Trinity/SM +Tripitaka/M +Tripoli/M +Trippe/M +Trisha/M +Tristan/M +Triton/M +Trobriand/M +Troilus/M +Trojan/MS +Trollope/M +Trondheim/M +Tropicana/M +Trotsky/M +Troy/M +Troyes +Truckee/M +Trudeau/M +Trudy/M +Truffaut/M +Trujillo/M +Truman/M +Trumbull/M +Trump/M +Truth/M +Tsimshian/M +Tsiolkovsky/M +Tsitsihar/M +Tsongkhapa/M +Tswana/M +Tu/M +Tuamotu/M +Tuareg/M +Tubman/M +Tucker/M +Tucson/M +Tucuman/M +Tudor/SM +Tue/S +Tues/M +Tuesday/MS +Tulane/M +Tull/M +Tulsa/M +Tulsidas/M +Tums/M +Tungus/M +Tunguska/M +Tunis/M +Tunisia/M +Tunisian/MS +Tunney/M +Tupi/M +Tupperware/M +Tupungato/M +Turgenev/M +Turin/M +Turing/M +Turk/SM +Turkestan/M +Turkey/M +Turkic/MS +Turkish/M +Turkmenistan/M +Turlock/M +Turner/M +Turpin/M +Tuscaloosa/M +Tuscan/M +Tuscany/M +Tuscarora/MS +Tuscon/M +Tuskegee/M +Tussaud/M +Tut/M +Tutankhamen/M +Tutsi/M +Tutu/M +Tuvalu/M +Tuvaluan +Twain/M +Tweed/M +Tweedledee/M +Tweedledum/M +Twila/M +Twinkies/M +Twitter/M +Twizzlers/M +Twp +Ty/M +Tycho/M +Tylenol/M +Tyler/M +Tyndale/M +Tyndall/M +Tyre/M +Tyree/M +Tyrolean +Tyrone/M +Tyson/M +U/M +UAR +UAW +UBS/M +UCLA/M +UFO/SM +UHF/M +UK/M +UL +UN/M +UNESCO/M +UNICEF/M +UNIX/M +UPC +UPI/M +UPS/M +URL/S +US/M +USA/M +USAF +USB +USCG +USDA/M +USIA +USMC +USN +USO +USP +USPS +USS +USSR/M +UT/M +UTC +UV/M +Ubangi/M +Ubuntu/M +Ucayali/M +Uccello/M +Udall/M +Ufa/M +Uganda/M +Ugandan/MS +Uighur/M +Ujungpandang/M +Ukraine/M +Ukrainian/SM +Ulster/M +Ultrasuede/M +Ulyanovsk/M +Ulysses/M +Umbriel/M +Underwood/M +Ungava/M +Unicode/M +Unilever/M +Union/SM +Unionist +Uniontown/M +Uniroyal/M +Unitarian/MS +Unitarianism/MS +Unitas/M +Unix/S +Unukalhai/M +Upanishads/M +Updike/M +Upjohn/M +Upton/M +Ur/M +Ural/SM +Urals/M +Urania/M +Uranus/M +Urban/M +Urdu/M +Urey/M +Uriah/M +Uriel/M +Uris/M +Urquhart/M +Ursa/M +Ursula/M +Ursuline/M +Uruguay/M +Uruguayan/MS +Urumqi/M +Usenet/MS +Ustinov/M +Ut +Utah/M +Utahan/MS +Ute/SM +Utica/M +Utopia/SM +Utopian/SM +Utrecht/M +Utrillo/M +Uzbek/M +Uzbekistan/M +Uzi/SM +V/M +VA +VAT/M +VAX +VAXes +VBA/M +VCR/M +VD/M +VDT +VDU +VF +VFW/M +VG +VGA +VHF/M +VHS +VI/M +VIP/SM +VISTA +VJ +VLF/M +VOA +VP +VT +VTOL +Va/M +Vacaville/M +Vader/M +Vaduz/M +Val/M +Valarie/M +Valdez/M +Valdosta/M +Valencia/SM +Valenti/M +Valentin/M +Valentine/M +Valentino/M +Valenzuela/M +Valeria/M +Valerian/M +Valerie/M +Valery/M +Valhalla/M +Valium/MS +Valkyrie/SM +Vallejo/M +Valletta/M +Valois/M +Valparaiso/M +Valvoline/M +Van/M +Vance/M +Vancouver/M +Vandal/MS +Vanderbilt/M +Vandyke/M +Vanessa/M +Vang/M +Vanuatu/M +Vanzetti/M +Varanasi/M +Varese/M +Vargas/M +Vaseline/SM +Vasquez/M +Vassar/M +Vatican/M +Vauban/M +Vaughan/M +Vaughn/M +Vazquez/M +Veblen/M +Veda/SM +Vedanta/M +Vega/SM +Vegas/M +Vegemite/M +Vela/M +Velasquez/M +Velazquez/M +Velcro/MS +Velez/M +Velma/M +Velveeta/M +Venetian/SM +Venezuela/M +Venezuelan/SM +Venice/M +Venn/M +Ventolin/M +Venus/MS +Venusian/M +Vera/M +Veracruz/M +Verde/M +Verdi/M +Verdun/M +Verizon/M +Verlaine/M +Vermeer/M +Vermont/ZMR +Vermonter/M +Vern/M +Verna/M +Verne/M +Vernon/M +Verona/M +Veronese/M +Veronica/M +Versailles/M +Vesalius/M +Vespasian/M +Vespucci/M +Vesta/M +Vesuvius/M +Viacom/M +Viagra/M +Vic/M +Vicente/M +Vichy/M +Vicki/M +Vickie/M +Vicksburg/M +Vicky/M +Victor/M +Victoria/M +Victorian/MS +Victorianism +Victorville/M +Victrola/M +Vidal/M +Vienna/M +Viennese/M +Vientiane/M +Vietcong/M +Vietminh/M +Vietnam/M +Vietnamese/M +Vijayanagar/M +Vijayawada/M +Viking/MS +Vila/M +Villa/SM +Villarreal/M +Villas/M +Villon/M +Vilma/M +Vilnius/M +Vilyui/M +Vince/M +Vincent/M +Vindemiatrix/M +Vineland/M +Vinson/M +Viola/M +Violet/M +Virgie/M +Virgil/M +Virginia/M +Virginian/SM +Virgo/SM +Visa/M +Visalia/M +Visayans/M +Vishnu/M +Visigoth/M +Visigoths +Vistula/M +Vitim/M +Vito/M +Vitus/M +Vivaldi/M +Vivekananda/M +Vivian/M +Vivienne/M +Vlad/M +Vladimir/M +Vladivostok/M +Vlaminck/M +Vlasic/M +VoIP +Vogue/M +Volcker/M +Voldemort/M +Volga/M +Volgograd/M +Volkswagen/M +Volstead/M +Volta/M +Voltaire/M +Volvo/M +Vonda/M +Vonnegut/M +Voronezh/M +Vorster/M +Voyager/M +Vt +Vuitton/M +Vulcan/M +Vulg +Vulgate/SM +W/MDT +WA +WAC +WASP/M +WATS/M +WC +WHO/M +WI +WMD +WNW/M +WP +WSW/M +WTO +WV +WW +WWI +WWII +WWW/M +WY +WYSIWYG +Wabash/M +Wac +Waco/M +Wade/M +Wagner/M +Wagnerian/M +Wahhabi/M +Waikiki/M +Waite/M +Wake/M +Waksman/M +Wald/MN +Waldemar/M +Walden/M +Waldensian/M +Waldheim/M +Waldo/M +Waldorf/M +Wales/M +Walesa/M +Walgreen/SM +Walgreens/M +Walker/M +Walkman/M +Wall/SMR +Wallace/M +Wallenstein/M +Waller/M +Wallis/M +Walloon/M +Walls/M +Walmart/M +Walpole/M +Walpurgisnacht/M +Walsh/M +Walt/MRZ +Walter/M +Walters/M +Walton/M +Wanamaker/M +Wanda/M +Wang/M +Wankel/M +Ward/M +Ware/MG +Warhol/M +Waring/M +Warner/M +Warren/M +Warsaw/M +Warwick/M +Wasatch/M +Wash/M +Washington/M +Washingtonian/MS +Wassermann/M +Waterbury/M +Waterford/M +Watergate/M +Waterloo/MS +Waters/M +Watertown/M +Watkins/M +Watson/M +Watsonville/M +Watt/SM +Watteau/M +Watts/M +Watusi/M +Waugh/M +Wausau/M +Wave +Wayne/M +Waynesboro/M +Weaver/M +Web/MR +Webb/M +Weber/M +Webern/M +Webster/MS +Wed/M +Weddell/M +Wedgwood/M +Wednesday/MS +Weeks/M +Wehrmacht/M +Wei/M +Weierstrass/M +Weill/M +Weinberg/M +Weirton/M +Weiss/M +Weissmuller/M +Weizmann/M +Weldon/M +Welland/M +Weller/M +Welles/M +Wellington/SM +Wells/M +Welsh/M +Welshman/M +Welshmen/M +Welshwoman +Wenatchee/M +Wendell/M +Wendi/M +Wendy/M +Wesak/M +Wesley/M +Wesleyan/M +Wessex/M +Wesson/M +West/SM +Western/MRS +Westinghouse/M +Westminster/M +Weston/M +Westphalia/M +Weyden/M +Wezen/M +Wharton/M +Wheaties/M +Wheatstone/M +Wheeler/M +Wheeling/M +Whig/SM +Whipple/M +Whirlpool/M +Whistler/M +Whitaker/M +White/SM +Whitefield/M +Whitehall/M +Whitehead/M +Whitehorse/M +Whiteley/M +Whitfield/M +Whitley/M +Whitman/M +Whitney/M +Whitsunday/MS +Whittier/M +WiFi +Wicca/M +Wichita/M +Wiemar/M +Wiesel/M +Wiesenthal/M +Wiggins/M +Wigner/M +Wii/M +Wikileaks +Wikipedia/M +Wilberforce/M +Wilbert/M +Wilbur/M +Wilburn/M +Wilcox/M +Wilda/M +Wilde/MR +Wilder/M +Wiles/M +Wiley/M +Wilford/M +Wilfred/M +Wilfredo/M +Wilhelm/M +Wilhelmina/M +Wilkerson/M +Wilkes/M +Wilkins/M +Wilkinson/M +Will/M +Willa/M +Willamette/M +Willard/M +Willemstad/M +William/SM +Williams/M +Williamsburg/M +Williamson/M +Williamsport/M +Willie/M +Willis/M +Willy/M +Wilma/M +Wilmer/M +Wilmington/M +Wilson/M +Wilsonian/M +Wilton/M +Wimbledon/M +Wimsey/M +Winchell/M +Winchester/MS +Windbreaker/M +Windex/M +Windhoek/M +Windows/M +Windsor/SM +Windward/M +Winesap/M +Winfred/M +Winfrey/M +Winifred/M +Winkle/M +Winnebago/M +Winnie/M +Winnipeg/M +Winston/M +Winters/M +Winthrop/M +Wis +Wisc +Wisconsin/M +Wisconsinite/MS +Wise/M +Witt/M +Wittgenstein/M +Witwatersrand/M +Wm/M +Wobegon/M +Wodehouse/M +Wolf/M +Wolfe/M +Wolff/M +Wolfgang/M +Wollongong/M +Wollstonecraft/M +Wolsey/M +Wolverhampton +Wonder/M +Wonderbra/M +Wong/M +Wood/SM +Woodard/M +Woodhull/M +Woodland/M +Woodrow/M +Woods/M +Woodstock/M +Woodward/M +Woolf/M +Woolite/M +Woolongong/M +Woolworth/M +Wooster/M +Wooten/M +Worcester/SM +Worcestershire/M +WordPress/M +Wordsworth/M +Workman/M +Worms/M +Wotan/M +Wovoka/M +Wozniak/M +Wozzeck/M +Wrangell/M +Wren/M +Wright/M +Wrigley/M +Wroclaw/M +Wu/M +Wuhan/M +Wurlitzer/M +Wyatt/M +Wycherley/M +Wycliffe/M +Wyeth/M +Wylie/M +Wynn/M +Wyo +Wyoming/M +Wyomingite/SM +X/M +XEmacs/M +XL/M +XML +XS +XXL +Xamarin/M +Xanadu/M +Xanthippe/M +Xavier/M +Xe/SM +Xenakis/M +Xenia/M +Xenophon/M +Xerox/MS +Xerxes/M +Xhosa/M +Xi'an/M +Xian/SM +Xiaoping/M +Ximenes/M +Xingu/M +Xinjiang/M +Xiongnu/M +Xizang/M +Xmas/MS +Xochipilli/M +Xuzhou/M +Y/M +YMCA/M +YMHA +YMMV +YT +YWCA/M +YWHA +Yacc/M +Yahoo/M +Yahtzee/M +Yahweh/M +Yakima/M +Yakut/M +Yakutsk/M +Yale/M +Yalow/M +Yalta/M +Yalu/M +Yamagata/M +Yamaha/M +Yamoussoukro/M +Yang/M +Yangon/M +Yangtze/M +Yank/SM +Yankee/SM +Yaobang/M +Yaounde/M +Yaqui/M +Yaren +Yaroslavl/M +Yataro/M +Yates/M +Yauco/M +Yb/M +Yeager/M +Yeats/M +Yekaterinburg/M +Yellowknife/M +Yellowstone/M +Yeltsin/M +Yemen/M +Yemeni/SM +Yemenite +Yenisei/M +Yerevan/M +Yerkes/M +Yesenia/M +Yevtushenko/M +Yggdrasil/M +Yiddish/M +Ymir/M +Yoda/M +Yoknapatawpha/M +Yoko/M +Yokohama/M +Yolanda/M +Yong/M +Yonkers/M +York/M +Yorkie/M +Yorkshire/MS +Yorktown/M +Yoruba/M +Yosemite/M +Yossarian/M +YouTube/M +Young/M +Youngstown/M +Ypres/M +Ypsilanti/M +Yuan/M +Yucatan/M +Yugo/M +Yugoslav/MS +Yugoslavia/M +Yugoslavian/SM +Yukon/M +Yule/SM +Yuletide/MS +Yuma/SM +Yunnan/M +Yuri/M +Yves/M +Yvette/M +Yvonne/M +Z/SMNXT +Zachariah/M +Zachary/M +Zachery/M +Zagreb/M +Zaire/M +Zairian +Zambezi/M +Zambia/M +Zambian/SM +Zamboni/M +Zamenhof/M +Zamora/M +Zane/M +Zanuck/M +Zanzibar/M +Zapata/M +Zaporozhye/M +Zapotec/M +Zappa/M +Zara/M +Zarathustra/M +Zealand/M +Zebedee/M +Zechariah/M +Zedekiah/M +Zedong/M +Zeffirelli/M +Zeke/M +Zelig/M +Zelma/M +Zen/M +Zenger/M +Zeno/M +Zephaniah/M +Zephyrhills/M +Zephyrus/M +Zest/M +Zeus/M +Zhdanov +Zhejiang/M +Zhengzhou/M +Zhivago/M +Zhukov/M +Zibo/M +Ziegfeld/M +Ziegler/M +Ziggy/M +Zika +Zimbabwe/M +Zimbabwean/SM +Zimmerman/M +Zinfandel/M +Zion/SM +Zionism/SM +Zionist/SM +Ziploc/M +Zn/M +Zoe/M +Zola/M +Zollverein/M +Zoloft/M +Zomba/M +Zorn/M +Zoroaster/M +Zoroastrian/MS +Zoroastrianism/SM +Zorro/M +Zosma/M +Zr/M +Zsigmondy/M +Zubenelgenubi/M +Zubeneschamali/M +Zukor/M +Zulu/SM +Zululand +Zuni/M +Zurich/M +Zwingli/M +Zworykin/M +Zyrtec/M +Zyuganov/M +Zzz +a/S +aah +aardvark/SM +ab/SDY +aback +abacus/MS +abaft +abalone/SM +abandon/LSDG +abandonment/M +abase/LGDS +abasement/M +abash/GLDS +abashed/UY +abashment/M +abate/LGDS +abated/U +abatement/M +abattoir/MS +abbe/SM +abbess/MS +abbey/MS +abbot/MS +abbr +abbrev/S +abbreviate/DSGNX +abbreviation/M +abdicate/GNDSX +abdication/M +abdomen/SM +abdominal +abduct/DSG +abductee/MS +abduction/SM +abductor/MS +abeam +aberrant +aberration/MS +aberrational +abet/S +abetted +abetting +abettor/SM +abeyance/M +abhor/S +abhorred +abhorrence/M +abhorrent/Y +abhorring +abidance/M +abide/GS +abiding/Y +ability/IEMS +abject/YP +abjection/M +abjectness/M +abjuration/SM +abjuratory +abjure/ZGDRS +abjurer/M +ablate/XGNVDS +ablation/M +ablative/MS +ablaze +able/UT +abler +abloom +ablution/SM +abnegate/GNDS +abnegation/M +abnormal/Y +abnormality/SM +aboard +abode/MS +abolish/GDS +abolition/M +abolitionism/M +abolitionist/SM +abominable +abominably +abominate/DSGNX +abomination/M +aboriginal/MS +aborigine/SM +aborning +abort/GVDS +abortion/MS +abortionist/MS +abortive/Y +abound/DSG +about +above/M +aboveboard +abracadabra/M +abrade/GDS +abrasion/MS +abrasive/MYPS +abrasiveness/M +abreast +abridge/DSG +abridgment/MS +abroad +abrogate/XGNDS +abrogation/M +abrogator/MS +abrupt/TPRY +abruptness/M +abs/M +abscess/MDSG +abscissa/SM +abscission/M +abscond/ZGSDR +absconder/M +abseil/MDSG +absence/SM +absent/DYSG +absentee/MS +absenteeism/M +absentminded/YP +absentmindedness/M +absinthe/M +absolute/PMYTNS +absoluteness/M +absolution/M +absolutism/M +absolutist/MS +absolve/DSG +absorb/AGDS +absorbance +absorbency/M +absorbent/SM +absorbing/Y +absorption/M +absorptive +abstain/DRZGS +abstainer/M +abstemious/PY +abstemiousness/M +abstention/MS +abstinence/M +abstinent +abstract/GSPMDY +abstracted/YP +abstractedness/M +abstraction/SM +abstractness/MS +abstruse/YP +abstruseness/M +absurd/TPRY +absurdist/MS +absurdity/SM +absurdness/M +abundance/SM +abundant/Y +abuse's +abuse/EGVDS +abuser/MS +abusive/YP +abusiveness/M +abut/SL +abutment/MS +abutted +abutting +abuzz +abysmal/Y +abyss/MS +abyssal +ac +acacia/MS +academe/M +academia/M +academic/SM +academical/Y +academician/MS +academy/SM +acanthus/MS +accede/GDS +accelerate/GNXDS +acceleration/M +accelerator/SM +accent/MDSG +accented/U +accentual +accentuate/GNDS +accentuation/M +accept/DSBG +acceptability/M +acceptableness/M +acceptably/U +acceptance/SM +acceptation/MS +accepted/U +access/MDSG +accessibility/IM +accessible/I +accessibly/I +accession/MDGS +accessorize/DSG +accessory/SM +accident/MS +accidental/SMY +acclaim/MDGS +acclamation/M +acclimate/DSGN +acclimation/M +acclimatization/M +acclimatize/DSG +acclivity/SM +accolade/SM +accommodate/XGNDS +accommodating/Y +accommodation/M +accompanied/U +accompaniment/MS +accompanist/SM +accompany/DSG +accomplice/SM +accomplish/DSLG +accomplished/U +accomplishment/MS +accord/GMDS +accordance/M +accordant +according/Y +accordion/MS +accordionist/MS +accost/GMDS +account/MDSBG +accountability/M +accountable/U +accountancy/M +accountant/MS +accounted/U +accounting/M +accouter/SGD +accouterments/M +accredit/SGD +accreditation/M +accredited/U +accretion/MS +accrual/MS +accrue/GDS +acct +acculturate/DSGN +acculturation/M +accumulate/XGNVDS +accumulation/M +accumulator/MS +accuracy/IM +accurate/IY +accurateness/M +accursed/P +accursedness/M +accusation/MS +accusative/MS +accusatory +accuse/ZGDRS +accuser/M +accusing/Y +accustom/DSG +accustomed/U +ace/DSMG +acerbate/DSG +acerbic +acerbically +acerbity/M +acetaminophen/M +acetate/MS +acetic +acetone/M +acetonic +acetyl +acetylene/M +ache/DSMG +achene/MS +achievable/U +achieve/BLZGDRS +achievement/SM +achiever/M +aching/Y +achoo/M +achromatic +achy/TR +acid/SMY +acidic +acidify/GDS +acidity/M +acidosis/M +acidulous +acknowledge/DSG +acknowledged/U +acknowledgment/SM +acme/SM +acne/M +acolyte/MS +aconite/MS +acorn/MS +acoustic/S +acoustical/Y +acoustics/M +acquaint/AGSD +acquaintance/SM +acquaintanceship/M +acquainted/U +acquiesce/DSG +acquiescence/M +acquiescent/Y +acquire/ZGBDRSL +acquirement/M +acquisition/MS +acquisitive/YP +acquisitiveness/M +acquit/S +acquittal/MS +acquitted +acquitting +acre/SM +acreage/MS +acrid/PTRY +acridity/M +acridness/M +acrimonious/YP +acrimoniousness/M +acrimony/M +acrobat/MS +acrobatic/S +acrobatically +acrobatics/M +acronym/MS +acrophobia/M +acropolis/MS +across +acrostic/SM +acrylamide +acrylic/MS +act's +act/ASDGV +acting/M +actinium/M +action/ASM +actionable +activate/ICANGSD +activation/ICAM +activator/MS +active's +active/IKY +activeness/M +actives +activism/M +activist/MS +activities +activity/IM +actor/AMS +actress/MS +actual/Y +actuality/SM +actualization/M +actualize/GDS +actuarial +actuary/SM +actuate/GNDS +actuation/M +actuator/SM +acuity/M +acumen/M +acupressure/M +acupuncture/M +acupuncturist/SM +acute/PMYTRS +acuteness/M +acyclovir/M +acyl +ad/SM +adage/MS +adagio/MS +adamant/MY +adapt/BZGVDRS +adaptability/M +adaptation/MS +adapter/M +adaption/S +add/SDRBZG +addend/MS +addenda +addendum/M +adder/M +addict/GVMDS +addiction/SM +addition/SM +additional/Y +additive/SM +addle/GDS +address's +address/AGDS +addressable +addressed/U +addressee/SM +adduce/GDS +adenine/M +adenocarcinoma +adenoid/SM +adenoidal +adept/MYPS +adeptness/M +adequacy/IM +adequate/IY +adequateness/M +adhere/GDS +adherence/M +adherent/SM +adhesion/M +adhesive/PSM +adhesiveness/M +adiabatic +adieu/MS +adios +adipose +adj +adjacency/M +adjacent/Y +adjectival/Y +adjective/MS +adjoin/GDS +adjourn/DGLS +adjournment/SM +adjudge/GDS +adjudicate/GNVXDS +adjudication/M +adjudicator/SM +adjudicatory +adjunct/MS +adjuration/MS +adjure/GDS +adjust/AGDSL +adjustable +adjuster/SM +adjustment/AMS +adjutant/SM +adman/M +admen +admin/S +administer/DGS +administrate/XDSGNV +administration/M +administrative/Y +administrator/MS +admirably +admiral/MS +admiralty/M +admiration/M +admire/BZGDRS +admirer/M +admiring/Y +admissibility/IM +admissible/I +admissibly +admission/AM +admissions +admit/AS +admittance/M +admitted/Y +admitting/A +admix/GDS +admixture/SM +admonish/LDSG +admonishment/MS +admonition/MS +admonitory +ado/M +adobe/MS +adolescence/SM +adolescent/SM +adopt/AGVDS +adoptable +adopter/MS +adoption/SM +adorableness/M +adorably +adoration/M +adore/BZGDRS +adorer/M +adoring/Y +adorn/LGDS +adorned/U +adornment/MS +adrenal/MS +adrenalin's +adrenaline/M +adrenergic +adrift +adroit/PY +adroitness/M +adsorb/SDG +adsorbent/MS +adsorption/SM +adulate/DSGN +adulation/M +adulator/MS +adulatory +adult/MS +adulterant/MS +adulterate/GNDS +adulterated/U +adulteration/M +adulterer/SM +adulteress/MS +adulterous +adultery/SM +adulthood/M +adumbrate/GNDS +adumbration/M +adv +advance/LDSMG +advancement/SM +advantage/EDSMG +advantageous/EY +advent/SM +adventitious/Y +adventure/DRSMZG +adventurer/M +adventuresome +adventuress/MS +adventurism +adventurist/S +adventurous/YP +adventurousness/M +adverb/SM +adverbial/SMY +adversarial +adversary/SM +adverse/PRYT +adverseness/M +adversity/SM +advert/SMDG +advertise/LZGDRS +advertised/U +advertisement/MS +advertiser/M +advertising/M +advertorial/SM +advice/M +advisability/IM +advisable/I +advisably +advise/LDRSZGB +advised/UY +advisement/M +adviser/M +advisor/SM +advisory/SM +advocacy/M +advocate/MGDS +advt +adware +adze/SM +aegis/M +aerate/DSGN +aeration/M +aerator/SM +aerial/SMY +aerialist/MS +aerie/MS +aerobatic/S +aerobatics/M +aerobic/S +aerobically +aerobics/M +aerodrome/MS +aerodynamic/S +aerodynamically +aerodynamics/M +aerogram/S +aeronautic/S +aeronautical +aeronautics/M +aerosol/MS +aerospace/M +aery +aesthete/MS +aesthetic/S +aesthetically +aestheticism/M +aesthetics/M +afar +affability/M +affable +affably +affair/MS +affect's +affect/EGDS +affectation/SM +affected/UY +affecting/Y +affection/EM +affectionate/Y +affections +afferent +affiance/GDS +affidavit/SM +affiliate's +affiliate/EGNDS +affiliated/U +affiliation/EM +affiliations +affine +affinity/SM +affirm/AGDS +affirmation/AMS +affirmative/MYS +affix/GMDS +afflatus/M +afflict/GDS +affliction/SM +affluence/M +affluent/Y +afford/GDSB +affordability +affordably +afforest/EGSD +afforestation/M +affray/MS +affront/GMDS +afghan/MS +aficionado/MS +afield +afire +aflame +afloat +aflutter +afoot +aforementioned +aforesaid +aforethought +afoul +afraid/U +afresh +aft/RZ +afterbirth/M +afterbirths +afterburner/MS +aftercare/M +aftereffect/MS +afterglow/SM +afterimage/MS +afterlife/M +afterlives +aftermarket/MS +aftermath/M +aftermaths +afternoon/MS +aftershave/SM +aftershock/SM +aftertaste/SM +afterthought/SM +afterward/S +afterword/MS +again +against +agape/M +agar/M +agate/MS +agave/M +age/DSMGJ +ageism/M +ageist/SM +ageless/YP +agelessness/M +agency/SM +agenda/SM +agenesis +agent/AMS +ageratum/M +agglomerate/DSMGNX +agglomeration/M +agglutinate/DSXGN +agglutination/M +aggrandize/GLDS +aggrandizement/M +aggravate/GNXDS +aggravating/Y +aggravation/M +aggregate/MGNDSX +aggregation/M +aggregator/SM +aggression/M +aggressive/PY +aggressiveness/M +aggressor/SM +aggrieve/DSG +aggro +aghast +agile/Y +agility/M +aging/M +agitate/XGNDS +agitation/M +agitator/MS +agitprop/M +agleam +aglitter +aglow +agnostic/MS +agnosticism/M +ago +agog +agonist/S +agonize/GDS +agonizing/Y +agony/SM +agoraphobia/M +agoraphobic/MS +agrarian/MS +agrarianism/M +agree/EBLDS +agreeableness/EM +agreeably/E +agreeing/E +agreement/ESM +agribusiness/MS +agricultural/Y +agriculturalist/MS +agriculture/M +agriculturist/MS +agronomic +agronomist/MS +agronomy/M +aground +ague/M +ah +aha +ahchoo +ahead +ahem +ahoy +aid/SMDG +aide/SM +aided/U +aigrette/MS +ail/SDLG +aileron/SM +ailment/SM +aim/SMDG +aimless/YP +aimlessness/M +ain't +air/SMDJG +airbag/MS +airbase/SM +airbed/S +airborne +airbrush/MDSG +airbus/MS +aircraft/M +aircraftman +aircraftmen +aircrew/S +airdrome/S +airdrop/SM +airdropped +airdropping +airfare/SM +airfield/SM +airflow/M +airfoil/SM +airfreight/M +airguns +airhead/SM +airily +airiness/M +airing/M +airless/P +airlessness/M +airletters +airlift/SGMD +airline/RSMZ +airliner/M +airlock/SM +airmail/GSMD +airman/M +airmen +airplane/MS +airplay/M +airport/SM +airship/SM +airshow/S +airsick/P +airsickness/M +airspace/M +airspeed +airstrike/MS +airstrip/SM +airtight +airtime/M +airwaves/M +airway/MS +airwoman +airwomen +airworthiness/M +airworthy/P +airy/PTR +aisle/MS +aitch/MS +ajar +aka +akimbo +akin +alabaster/M +alack +alacrity/M +alarm/GMDS +alarming/Y +alarmist/SM +alas +alb/SM +albacore/SM +albatross/MS +albeit +albinism/M +albino/MS +album/MNS +albumen/M +albumin/M +albuminous +alchemist/SM +alchemy/M +alcohol/SM +alcoholic/MS +alcoholically +alcoholism/M +alcove/MS +alder/MS +alderman/M +aldermen +alderwoman/M +alderwomen +ale/SMV +aleatory +alehouse/SM +alembic/SM +alert/GMDYPS +alertness/M +alewife/M +alewives +alfalfa/M +alfresco +alga/M +algae +algal +algebra/SM +algebraic +algebraically +algorithm/SM +algorithmic +alias/GMDS +alibi/GMDS +alien/BGMDS +alienable/IU +alienate/DSGN +alienation/M +alienist/SM +alight/GDS +align/ALGDS +aligned/U +aligner/MS +alignment/AMS +alike/U +aliment/MDSG +alimentary +alimony/M +aliveness/M +aliyah/M +aliyahs +alkali/M +alkalies +alkaline +alkalinity/M +alkalize/DSG +alkaloid/SM +alkyd/MS +all/M +allay/GDS +allegation/MS +allege/GDS +alleged/Y +allegiance/MS +allegoric +allegorical/Y +allegorist/MS +allegory/SM +allegretto/MS +allegro/MS +allele/MS +alleluia/SM +allergen/SM +allergenic +allergic +allergically +allergist/SM +allergy/SM +alleviate/DSGN +alleviation/M +alley/MS +alleyway/SM +alliance/SM +alligator/MS +alliterate/DSXGNV +alliteration/M +alliterative/Y +allocate/ADSGN +allocation/AM +allocations +allot/LS +allotment/SM +allotted +allotting +allover +allow/EGDS +allowable/U +allowably +allowance/SM +alloy/GMDS +alloyed/U +allspice/M +allude/GDS +allure/MGLDS +allurement/MS +alluring/Y +allusion/SM +allusive/PY +allusiveness/M +alluvial/M +alluvium/SM +ally/GDSM +almanac/SM +almighty +almond/MS +almoner/SM +almost +alms/M +almshouse/MS +aloe/SM +aloft +aloha/MS +alone +along +alongshore +alongside +aloof/PY +aloofness/M +aloud +alp/SM +alpaca/MS +alpha/MS +alphabet/SM +alphabetic +alphabetical/Y +alphabetization/SM +alphabetize/ZGDRS +alphabetizer/M +alphanumeric +alphanumerical/Y +alpine/S +already +alright +also +alt/S +altar/MS +altarpiece/SM +alter/GDBS +alterable/U +alteration/MS +altercation/SM +altered/U +alternate/DSMYGNVX +alternation/M +alternative/MYS +alternator/SM +although +altimeter/MS +altitude/MS +alto/SM +altogether +altruism/M +altruist/SM +altruistic +altruistically +alum/SM +alumina/M +aluminum/M +alumna/M +alumnae +alumni +alumnus/M +alveolar/S +always +am/N +amalgam/SM +amalgamate/XGNDS +amalgamation/M +amanuenses +amanuensis/M +amaranth/M +amaranths +amaretto/M +amaryllis/MS +amass/GDS +amateur/SM +amateurish/YP +amateurishness/M +amateurism/M +amatory +amaze/LMGDS +amazement/M +amazing/Y +amazon/MS +amazonian +ambassador/SM +ambassadorial +ambassadorship/MS +ambassadress/MS +amber/M +ambergris/M +ambiance/MS +ambidexterity/M +ambidextrous/Y +ambient +ambiguity/SM +ambiguous/UY +ambit +ambition/MS +ambitious/YP +ambitiousness/M +ambivalence/M +ambivalent/Y +amble/MZGDRS +ambler/M +ambrosia/M +ambrosial +ambulance/MS +ambulanceman +ambulancemen +ambulancewoman +ambulancewomen +ambulant +ambulate/DSXGN +ambulation/M +ambulatory/SM +ambuscade/MGDS +ambush/GMDS +ameliorate/GNVDS +amelioration/M +amen/B +amenability/M +amenably +amend/BLGDS +amendment/SM +amenity/SM +amerce/GLDS +amercement/SM +americium/M +amethyst/SM +amiability/M +amiable +amiably +amicability/M +amicable +amicably +amid +amide/MS +amidship/S +amigo/MS +amine/S +amino +amiss +amitriptyline +amity/M +ammeter/SM +ammo/M +ammonia/M +ammonium +ammunition/M +amnesia/M +amnesiac/MS +amnesic/SM +amnesty/GDSM +amniocenteses +amniocentesis/M +amnion/MS +amniotic +amoeba/MS +amoebae +amoebic +amok +among +amontillado/SM +amoral/Y +amorality/M +amorous/YP +amorousness/M +amorphous/PY +amorphousness/M +amortization/SM +amortize/DSGB +amount/GMDS +amour/MS +amoxicillin +amp/SMY +amperage/M +ampere/MS +ampersand/MS +amphetamine/SM +amphibian/MS +amphibious/Y +amphitheater/SM +amphora/M +amphorae +ampicillin +ample/TR +amplification/M +amplifier/M +amplify/NDRSXZG +amplitude/SM +ampule/MS +amputate/GNDSX +amputation/M +amputee/MS +amt +amulet/MS +amuse/LGDS +amusement/MS +amusing/Y +amygdala +amylase/M +amyloid +an/CS +anabolism/M +anachronism/SM +anachronistic +anachronistically +anaconda/SM +anaerobe/SM +anaerobic +anaerobically +anagram/MS +anal/Y +analgesia/M +analgesic/SM +analog/MS +analogical/Y +analogize/GDS +analogous/YP +analogousness/M +analogue/SM +analogy/SM +analysand/MS +analyses/A +analysis/AM +analyst/SM +analytic/S +analytical/Y +analyzable +analyze/ADSG +analyzer/SM +anapest/SM +anapestic/MS +anarchic +anarchically +anarchism/M +anarchist/MS +anarchistic +anarchy/M +anathema/SM +anathematize/DSG +anatomic +anatomical/Y +anatomist/SM +anatomize/DSG +anatomy/SM +ancestor/SM +ancestral/Y +ancestress/MS +ancestry/SM +anchor/MDGS +anchorage/MS +anchorite/MS +anchorman/M +anchormen +anchorpeople +anchorperson/SM +anchorwoman/M +anchorwomen +anchovy/SM +ancient/SPMRYT +ancientness/M +ancillary/SM +and +andante/SM +andiron/SM +androgen/M +androgenic +androgynous +androgyny/M +android/SM +anecdotal/Y +anecdote/MS +anemia/M +anemic +anemically +anemometer/SM +anemone/SM +anent +anesthesia/M +anesthesiologist/SM +anesthesiology/M +anesthetic/SM +anesthetist/MS +anesthetization/M +anesthetize/GDS +aneurysm/SM +anew +angel/MS +angelfish/MS +angelic +angelica/M +angelical/Y +anger/GMDS +angina/M +angioplasty/SM +angiosperm/SM +angle/MZGDRS +angler/M +angleworm/MS +anglicism/S +anglicize/GDS +angling/M +anglophile/S +anglophone/S +angora/MS +angostura +angrily +angry/TR +angst/M +angstrom/MS +anguish/GMDS +angular +angularity/SM +angulation +anhydrous +aniline/M +anilingus +animadversion/MS +animadvert/GSD +animal/MS +animalcule/SM +animate/ADSGN +animated/Y +animation/AM +animations +animator/MS +anime/M +animism/M +animist/SM +animistic +animosity/SM +animus/M +anion/MS +anionic +anise/M +aniseed/M +anisette/M +ankh/M +ankhs +ankle/MS +anklebone/MS +anklet/MS +annalist/SM +annals/M +anneal/GDS +annelid/MS +annex/GMDS +annexation/MS +annihilate/DSGN +annihilation/M +annihilator/SM +anniversary/SM +annotate/DSXGNV +annotation/M +annotator/MS +announce/DRSLZG +announced/U +announcement/MS +announcer/M +annoy/GDS +annoyance/MS +annoying/Y +annual/MYS +annualized +annuitant/SM +annuity/SM +annul/LS +annular +annulled +annulling +annulment/SM +annulus +annunciation/SM +anode/MS +anodize/GDS +anodyne/MS +anoint/GDLS +anointment/M +anomalous/Y +anomaly/SM +anon/S +anonymity/M +anonymous/Y +anopheles/M +anorak/MS +anorectic/SM +anorexia/M +anorexic/MS +another +answer/BMDGS +answerable/U +answered/U +answerphone/S +ant/SMD +antacid/SM +antagonism/SM +antagonist/SM +antagonistic +antagonistically +antagonize/DSG +antarctic +ante/SM +anteater/MS +antebellum +antecedence/M +antecedent/SM +antechamber/SM +antedate/GDS +antediluvian +anteing +antelope/MS +antenatal +antenna/SM +antennae +anterior +anteroom/MS +anthem/MS +anther/MS +anthill/SM +anthologist/SM +anthologize/DSG +anthology/SM +anthracite/M +anthrax/M +anthropocentric +anthropoid/MS +anthropological/Y +anthropologist/SM +anthropology/M +anthropomorphic +anthropomorphically +anthropomorphism/M +anthropomorphize +anthropomorphous +anti/SM +antiabortion +antiabortionist/MS +antiaircraft +antibacterial/MS +antibiotic/MS +antibody/SM +antic/MS +anticancer +anticipate/GNXDS +anticipated/U +anticipation/M +anticipatory +anticked +anticking +anticlerical +anticlimactic +anticlimactically +anticlimax/MS +anticline/SM +anticlockwise +anticoagulant/MS +anticommunism/M +anticommunist/SM +anticyclone/SM +anticyclonic +antidemocratic +antidepressant/MS +antidote/MS +antifascist/MS +antiferromagnetic +antifreeze/M +antigen/SM +antigenic +antigenicity/M +antihero/M +antiheroes +antihistamine/SM +antiknock/M +antilabor +antilogarithm/SM +antimacassar/MS +antimalarial +antimatter/M +antimicrobial +antimissile +antimony/M +antineutrino/SM +antineutron/MS +antinuclear +antioxidant/MS +antiparticle/SM +antipasti +antipasto/MS +antipathetic +antipathy/SM +antipersonnel +antiperspirant/SM +antiphon/SM +antiphonal/MYS +antipodal/S +antipodean/MS +antipodes/M +antipollution +antipoverty +antiproton/MS +antiquarian/SM +antiquarianism/M +antiquary/SM +antiquate/GDS +antique/DSMG +antiquity/SM +antirrhinum/S +antiscience +antisemitic +antisemitism/M +antisepsis/M +antiseptic/SM +antiseptically +antiserum/MS +antislavery +antisocial/Y +antispasmodic/MS +antisubmarine +antitank +antitheses +antithesis/M +antithetic +antithetical/Y +antitoxin/MS +antitrust +antivenin/MS +antivenom +antiviral/MS +antivirus +antivivisectionist/MS +antiwar +antler/MDS +antonym/SM +antonymous +antrum +antsy/TR +anus/MS +anvil/MS +anxiety/SM +anxious/YP +anxiousness/M +any +anybody/SM +anyhow +anymore +anyone/M +anyplace +anything/SM +anytime +anyway/S +anywhere +anywise +aorta/MS +aortic +apace +apart +apartheid/M +apartment/MS +apathetic +apathetically +apathy/M +apatite/M +ape/DSMG +apelike +aperitif/MS +aperture/SM +apex/MS +aphasia/M +aphasic/MS +aphelia +aphelion/SM +aphid/MS +aphorism/MS +aphoristic +aphoristically +aphrodisiac/SM +apiarist/SM +apiary/SM +apical/Y +apiece +apish/Y +aplenty +aplomb/M +apocalypse/SM +apocalyptic +apocrypha/M +apocryphal/Y +apogee/MS +apolitical/Y +apologetic/U +apologetically +apologia/SM +apologist/MS +apologize/GDS +apology/SM +apoplectic +apoplexy/SM +apoptosis +apoptotic +apostasy/SM +apostate/SM +apostatize/GDS +apostle/MS +apostleship/M +apostolic +apostrophe/MS +apothecary/SM +apothegm/SM +apotheoses +apotheosis/M +app/SM +appall/GDS +appalling/Y +appaloosa/MS +apparatchik/S +apparatus/MS +apparel/MDGS +apparent/Y +apparition/SM +appeal/GMDS +appealing/UY +appear/AESDG +appearance/EAMS +appease/LZGDRS +appeasement/SM +appeaser/M +appellant/SM +appellate/XN +appellation/M +append/GDS +appendage/SM +appendectomy/SM +appendices +appendicitis/M +appendix/MS +appertain/GDS +appetite/SM +appetizer/MS +appetizing/Y +applaud/ZGDRS +applauder/M +applause/M +apple/MS +applejack/M +applesauce/M +applet/MS +appliance/SM +applicability/M +applicable/I +applicably +applicant/SM +application/AM +applicator/SM +applier/MS +applique/DSM +appliqueing +apply/ANXGDS +appoint/AELSVGD +appointee/SM +appointment's/A +appointment/ESM +apportion/AGDLS +apportionment/AM +appose/GDS +apposite/YNVP +appositeness/M +apposition/M +appositive/SM +appraisal/AMS +appraise/ADSG +appraiser/MS +appreciable/I +appreciably/I +appreciate/DSXGNV +appreciated/U +appreciation/M +appreciative/Y +appreciator/MS +appreciatory +apprehend/GDS +apprehension/MS +apprehensive/YP +apprehensiveness/M +apprentice/DSMG +apprenticeship/MS +apprise/GDS +apprize/GDS +approach/GBMDS +approachable/UI +approbation/EM +approbations +appropriate/PYGNXDS +appropriated/U +appropriateness/IM +appropriation/M +appropriator/SM +approval/EM +approvals +approve/EGDS +approved/U +approving/EY +approx +approximate/DSXYGN +approximation/M +appurtenance/SM +appurtenant +apricot/MS +apron/MS +apropos +apse/SM +apt/IYPT +apter +aptitude/SM +aptness/IM +aqua/SM +aquaculture/M +aqualung/MS +aquamarine/SM +aquanaut/MS +aquaplane/MGDS +aquarium/MS +aquatic/SM +aquatically +aquatics/M +aquatint/S +aquavit/M +aqueduct/MS +aqueous +aquifer/SM +aquiline +arabesque/MS +arability/M +arachnid/MS +arachnophobia +arbiter/SM +arbitrage/MZGDRS +arbitrager/M +arbitrageur/SM +arbitrament/SM +arbitrarily +arbitrariness/M +arbitrary/P +arbitrate/GNDS +arbitration/M +arbitrator/MS +arbor/MS +arboreal +arboretum/SM +arborvitae/SM +arbutus/MS +arc/SMDG +arcade/MS +arcane +arch/PZTGVMDRSY +archaeological/Y +archaeologist/SM +archaeology/M +archaic +archaically +archaism/MS +archaist/MS +archangel/MS +archbishop/SM +archbishopric/SM +archdeacon/SM +archdiocesan +archdiocese/MS +archduchess/MS +archduke/MS +archenemy/SM +archer/M +archery/M +archetypal +archetype/MS +archfiend/MS +archiepiscopal +archipelago/MS +architect/SM +architectonic/S +architectonics/M +architectural/Y +architecture/MS +architrave/SM +archival +archive/DSMG +archivist/MS +archness/M +archway/SM +arctic/MS +ardent/Y +ardor/MS +arduous/YP +arduousness/M +are/SMB +area/SM +areal +aren't +arena/MS +argent/M +arginine +argon/M +argosy/SM +argot/MS +arguable/IU +arguably/U +argue/ZGDRS +arguer/M +argument/MS +argumentation/M +argumentative/PY +argumentativeness/M +argyle/MS +aria/SM +arid/Y +aridity/M +aright +arise/GS +arisen +aristocracy/SM +aristocrat/SM +aristocratic +aristocratically +arithmetic/M +arithmetical/Y +arithmetician/MS +ark/SM +arm's +arm/EAGDS +armada/MS +armadillo/SM +armament/AEM +armaments +armature/MS +armband/MS +armchair/MS +armed/U +armful/MS +armhole/SM +armistice/SM +armlet/MS +armload/S +armor/ZGMDRS +armored/U +armorer/M +armorial +armory/SM +armpit/MS +armrest/SM +army/SM +aroma/MS +aromatherapist/MS +aromatherapy/M +aromatic/MS +aromatically +arose +around +arousal/M +arouse/GDS +arpeggio/MS +arr +arraign/DGSL +arraignment/SM +arrange/AESDLG +arrangement's/E +arrangement/ASM +arranger/SM +arrant +arras/MS +array/EGMDS +arrears/M +arrest/AGMDS +arrhythmia/M +arrhythmic +arrhythmical +arrival/MS +arrive/GDS +arrogance/M +arrogant/Y +arrogate/GNDS +arrogation/M +arrow/MS +arrowhead/MS +arrowroot/M +arroyo/MS +arsed +arsenal/MS +arsenic/M +arsing +arson/M +arsonist/SM +art/SM +arterial +arteriole/MS +arteriosclerosis/M +artery/SM +artful/PY +artfulness/M +arthritic/MS +arthritis/M +arthropod/MS +arthroscope/SM +arthroscopic +arthroscopy +artichoke/SM +article/MDS +articulacy/I +articular +articulate/YGNPDSX +articulateness/IM +articulation/M +artifact/SM +artifice/RSMZ +artificer/M +artificial/Y +artificiality/M +artillery/M +artilleryman/M +artillerymen +artiness/M +artisan/MS +artist/MS +artiste/MS +artistic/I +artistically +artistry/M +artless/PY +artlessness/M +artsy/TR +artwork/MS +arty/PTR +arugula +arum/SM +asap +asbestos/M +ascend/AGDS +ascendance/M +ascendancy/M +ascendant/SM +ascension/MS +ascent/MS +ascertain/GDSBL +ascertainment/M +ascetic/MS +ascetically +asceticism/M +ascot/MS +ascribe/GBDS +ascription/M +aseptic +aseptically +asexual/Y +asexuality/M +ash/MDNSG +ashamed/UY +ashcan/MS +ashlar/MS +ashore +ashram/MS +ashtray/SM +ashy/TR +aside/MS +asinine/Y +asininity/SM +ask/SDG +askance +asked/U +askew +aslant +asleep +asocial +asp/SMNX +asparagus/M +aspartame/M +aspect/MS +aspen/M +asperity/SM +aspersion/MS +asphalt/MDGS +asphodel/SM +asphyxia/M +asphyxiate/DSXGN +asphyxiation/M +aspic/MS +aspidistra/MS +aspirant/MS +aspirate/MGNDSX +aspiration/M +aspirator/SM +aspire/GDS +aspirin/MS +ass/MS +assail/GBDS +assailable/U +assailant/SM +assassin/SM +assassinate/GNXDS +assassination/M +assault/MDRGS +assay/ZGMDRS +assayer/M +assemblage/SM +assemble/AEGSD +assembler/MS +assemblies +assembly/AM +assemblyman/M +assemblymen +assemblywoman/M +assemblywomen +assent/GMDS +assert/AGVDS +assertion/AM +assertions +assertive/YP +assertiveness/M +assess/ALGDS +assessment/ASM +assessor/MS +asset/MS +asseverate/DSGN +asseveration/M +asshole/MS! +assiduity/M +assiduous/PY +assiduousness/M +assign's +assign/ALGDS +assignable +assignation/MS +assigned/U +assignee/M +assigner/MS +assignment/AMS +assignor/MS +assimilate/DSGN +assimilated/U +assimilation/M +assist/GVMDS +assistance/M +assistant/SM +assisted/U +assize/MS +assn +assoc +associate's +associate/EDSGNV +association/EM +associations +associativity +assonance/M +assonant/MS +assort/GLDS +assortative +assortment/MS +asst +assuage/GDS +assume/BGDS +assumption/SM +assumptive +assurance/ASM +assure/AGDS +assured/MYS +astatine/M +aster/EMS +asterisk/GMDS +astern +asteroid/MS +asthma/M +asthmatic/SM +asthmatically +astigmatic +astigmatism/SM +astir +astonish/DSLG +astonishing/Y +astonishment/M +astound/GDS +astounding/Y +astraddle +astrakhan/M +astral +astray +astride +astringency/M +astringent/SMY +astrolabe/SM +astrologer/SM +astrological/Y +astrologist/MS +astrology/M +astronaut/MS +astronautic/S +astronautical +astronautics/M +astronomer/SM +astronomic +astronomical/Y +astronomy/M +astrophysical +astrophysicist/MS +astrophysics/M +astute/PYTR +astuteness/M +asunder +asylum/SM +asymmetric +asymmetrical/Y +asymmetry/SM +asymptomatic +asymptotic +asymptotically +asynchronous/Y +at +atavism/M +atavist/SM +atavistic +ataxia/M +ataxic/MS +ate +atelier/SM +atheism/M +atheist/MS +atheistic +atherosclerosis/M +atherosclerotic +athirst +athlete/MS +athletic/S +athletically +athleticism +athletics/M +athwart +atilt +atishoo +atlas/MS +atmosphere/MS +atmospheric/S +atmospherically +atmospherics/M +atoll/MS +atom/SM +atomic +atomically +atomize/ZGDRS +atomizer/M +atonal/Y +atonality/M +atone/LGDS +atonement/M +atop +atria +atrial +atrioventricular +atrium/M +atrocious/PY +atrociousness/M +atrocity/SM +atrophy/DSMG +atropine/M +attach/ALGDS +attache/BM +attached/U +attachment/AM +attachments +attack/ZGMDRS +attacker/M +attain/AGDS +attainability/M +attainable/U +attainder/M +attainment/SM +attar/M +attempt's +attempt/ASDG +attend/SDRZG +attendance/SM +attendant/SM +attended/U +attendee/SM +attention/IM +attentions +attentive/IPY +attentiveness/IM +attenuate/DSGN +attenuation/M +attest/SDG +attestation/SM +attested/U +attic/SM +attire/DSMG +attitude/SM +attitudinal +attitudinize/GDS +attn +attorney/MS +attract/SGVDB +attractant/MS +attraction/MS +attractive/UY +attractiveness/M +attribute/DSMGNVBX +attributed/U +attribution/M +attributive/MYS +attrition/M +attune/DSG +atty +atwitter +atypical/Y +aubergine/S +auburn/M +auction/MDGS +auctioneer/SM +audacious/YP +audaciousness/M +audacity/M +audibility/IM +audible/MS +audibly/I +audience/MS +audio/MS +audiological +audiologist/SM +audiology/M +audiometer/SM +audiophile/SM +audiotape/SM +audiovisual/S +audiovisuals/M +audit/GMDS +audition/SMDG +auditor/MS +auditorium/SM +auditory +auger/MS +aught/MS +augment/DRZGS +augmentation/MS +augmentative +augmenter/M +augur/GMDS +augury/SM +august/PTRY +augustness/M +auk/SM +aunt/SM +auntie/SM +aura/MS +aural/Y +aureole/SM +aureus +auricle/SM +auricular +aurora/SM +auscultate/GNDSX +auscultation/M +auspice/SM +auspicious/IY +auspiciousness/M +austere/RYT +austerity/SM +austral +authentic/IU +authentically +authenticate/XGNDS +authenticated/U +authentication/M +authenticity/M +author/SMDG +authoress/MS +authorial +authoritarian/MS +authoritarianism/M +authoritative/YP +authoritativeness/M +authority/SM +authorization/MS +authorize/AGDS +authorized/U +authorship/M +autism/M +autistic +auto/MS +autobahn/SM +autobiographer/SM +autobiographic +autobiographical/Y +autobiography/SM +autoclave/MS +autocracy/SM +autocrat/SM +autocratic +autocratically +autocross +autodidact/SM +autograph/MDG +autographs +autoimmune +autoimmunity/M +automaker/SM +automate/GNDS +automatic/SM +automatically +automation/M +automatism/M +automatize/GDS +automaton/SM +automobile/DSMG +automotive +autonomic +autonomous/Y +autonomy/M +autopilot/SM +autopsy/GDSM +autosuggestion +autoworker/MS +autumn/SM +autumnal +aux +auxiliary/SM +auxin/M +av/RZ +avail/BGMDS +availability/UM +available/U +avalanche/SM +avarice/M +avaricious/Y +avast +avatar/MS +avaunt +avdp +ave +avenge/ZGDRS +avenger/M +avenue/MS +average/MYGDS +averred +averring +averse/XN +aversion/M +avert/GDS +avg +avian +aviary/SM +aviation/M +aviator/MS +aviatrices +aviatrix/MS +avid/Y +avidity/M +avionic/S +avionics/M +avitaminosis/M +avocado/SM +avocation/MS +avocational +avoid/SDGB +avoidable/U +avoidably/U +avoidance/M +avoidant +avoirdupois/M +avouch/DSG +avow/EDGS +avowal/ESM +avowed/Y +avuncular/Y +aw +await/GDS +awake/GS +awaken/AGDS +awakening/SM +award/GMDS +awardee/S +aware/UP +awareness/UM +awash +away +awe/DSMG +aweigh +awesome/YP +awesomeness/M +awestruck +awful/YP +awfuller +awfullest +awfulness/M +awhile +awkward/RYPT +awkwardness/M +awl/SM +awn/GJSM +awning/M +awoke +awoken +awry +ax/MDSG +axial/Y +axiom/SM +axiomatic +axiomatically +axis/M +axle/MS +axletree/SM +axolotl/SM +axon/MS +ayah/M +ayahs +ayatollah/M +ayatollahs +aye/SM +azalea/SM +azimuth/M +azimuths +azure/SM +b/KDT +baa/SMDG +babble/MZGDRS +babbler/M +babe/SM +babel/MS +baboon/MS +babushka/SM +baby/TGDRSM +babyhood/M +babyish +babysat +babysit/S +babysitter/MS +babysitting/M +baccalaureate/SM +baccarat/M +bacchanal/MS +bacchanalia/M +bacchanalian/MS +baccy +bachelor/SM +bachelorhood/M +bacillary +bacilli +bacillus/M +back/SJZGMDR +backache/MS +backbench/S +backbit +backbite/ZGRS +backbiter/M +backbitten +backboard/SM +backbone/MS +backbreaking +backchat +backcloth +backcloths +backcomb/DSG +backdate/GDS +backdoor +backdrop/MS +backer/M +backfield/SM +backfire/MGDS +backgammon/M +background/MRZS +backgrounder/M +backhand/MDRSZG +backhanded/Y +backhander/M +backhoe/MS +backing/M +backlash/MS +backless +backlog/MS +backlogged +backlogging +backpack/ZGMDRS +backpacker/M +backpacking/M +backpedal/SDG +backrest/SM +backroom/S +backscratching/M +backseat/SM +backside/SM +backslapper/SM +backslapping/M +backslash/MS +backslid +backslide/RSZG +backslider/M +backspace/DSMG +backspin/M +backstabber/MS +backstabbing +backstage/M +backstair/S +backstop/SM +backstopped +backstopping +backstory/S +backstreet/S +backstretch/MS +backstroke/MGDS +backtalk/M +backtrack/SDG +backup/MS +backward/PSY +backwardness/M +backwash/M +backwater/SM +backwoods/M +backwoodsman/M +backwoodsmen +backyard/SM +bacon/M +bacteria/M +bacterial +bactericidal +bactericide/SM +bacteriologic +bacteriological +bacteriologist/SM +bacteriology/M +bacterium/M +bad/MYP +badder +baddest +baddie/MS +bade +badge/MZRS +badger/GMD +badinage/M +badlands/M +badman/M +badmen +badminton/M +badmouth/GD +badmouths +badness/M +baffle/MZGDRSL +bafflement/M +baffler/M +bag/SM +bagatelle/SM +bagel/MS +bagful/MS +baggage/M +bagged +baggie/M +baggily +bagginess/M +bagging +baggy/PTRS +bagpipe/MZRS +bagpiper/M +baguette/MS +bah +baht/SM +bail/SBGMD +bailey/S +bailiff/S +bailiwick/MS +bailout/SM +bailsman/M +bailsmen +bairn/MS +bait/SGMD +baize/M +bake/DRSMZG +baked/U +baker/M +bakery/SM +bakeshop/MS +baklava/M +baksheesh/M +balaclava/MS +balalaika/MS +balance's +balance/UDSG +balboa/SM +balcony/SM +bald/STGPDRY +balderdash/M +baldfaced +baldness/M +baldric/SM +baldy/S +bale/DRSMZG +baleen/M +baleful/PY +balefulness/M +baler/M +balk/SGMD +balky/RT +ball/SGMD +ballad/SM +balladeer/MS +balladry/M +ballast/GSMD +ballcock/MS +ballerina/SM +ballet/SM +balletic +ballgame/MS +ballgirl/S +ballgown/S +ballistic/S +ballistics/M +balloon/SGMD +balloonist/MS +ballot/SMDG +ballpark/MS +ballplayer/MS +ballpoint/MS +ballroom/MS +balls/DSG +ballsy/RT +bally +ballyhoo/SMDG +balm/SM +balminess/M +balmy/RTP +baloney/M +balsa/MS +balsam/SM +balsamic +baluster/SM +balustrade/MS +bamboo/SM +bamboozle/DSG +ban/SM +banal/Y +banality/SM +banana/SM +band's +band/ESGD +bandage/DSMG +bandanna/MS +bandbox/MS +bandeau/M +bandeaux +bandit/SM +banditry/M +bandleader/S +bandmaster/SM +bandoleer/SM +bandsman/M +bandsmen +bandstand/SM +bandwagon/SM +bandwidth +bandwidths +bandy/DRSTG +bane/SM +baneful +bang/SGMDR +bangle/SM +bani +banish/GLDS +banishment/M +banister/SM +banjo/MS +banjoist/SM +bank/SZGBMDR +bankbook/SM +bankcard/SM +banker/M +banking/M +banknote/SM +bankroll/SGMD +bankrupt/SGMD +bankruptcy/SM +banned +banner/SM +banning +bannock/MS +banns/M +banquet/ZGMDRS +banqueter/M +banquette/SM +banshee/MS +bantam/SM +bantamweight/SM +banter/GSMD +bantering/Y +banyan/SM +banzai/SM +baobab/SM +bap/S +baptism/MS +baptismal +baptist/S +baptistery/SM +baptize/ZGDRS +baptized/U +baptizer/M +bar's +bar/ECUTS +barb/SZGMDR +barbacoa +barbarian/SM +barbarianism/MS +barbaric +barbarically +barbarism/SM +barbarity/SM +barbarize/DSG +barbarous/Y +barbecue/DSMG +barbel/SM +barbell/MS +barber/GMD +barberry/SM +barbershop/MS +barbie/S +barbiturate/SM +barbwire/M +barcarole/SM +bard/SM +bardic +bare/DRSPYG +bareback/D +barefaced/Y +barefoot/D +barehanded +bareheaded +barelegged +bareness/M +barf/SGMDY +barfly/SM +bargain/MDRZGS +bargainer/M +barge/MGDS +bargeman/M +bargemen +barhop/S +barhopped +barhopping +barista/MS +baritone/MS +barium/M +bark's +bark/CSGD +barkeep/ZMRS +barkeeper/M +barker/SM +barley/M +barmaid/MS +barman/M +barmen +barmy/RT +barn/SM +barnacle/MDS +barney/S +barnstorm/SDRZG +barnstormer/M +barnyard/SM +barometer/MS +barometric +barometrically +baron/MS +baronage/MS +baroness/MS +baronet/MS +baronetcy/SM +baronial +barony/SM +baroque/M +barrack/MDGS +barracuda/SM +barrage/MGDS +barre/MGJDS +barred/UEC +barrel/GSMD +barren/TPSMR +barrenness/M +barrette/SM +barricade/MGDS +barrier/MS +barring/ECU +barrio/SM +barrister/MS +barroom/MS +barrow/SM +bartender/SM +barter/ZGSMDR +barterer/M +baryon/SM +basal/Y +basalt/M +basaltic +base's +base/CDSLTG +baseball/SM +baseboard/MS +baseless +baseline/MS +basely +baseman/M +basemen +basement/CMS +baseness/M +baser +bash/GMDS +bashful/PY +bashfulness/M +bashing/M +basic/MS +basically +basil/M +basilica/MS +basilisk/MS +basin/MS +basinful/MS +basis/M +bask/SGD +basket/SM +basketball/MS +basketry/M +basketwork/M +basque/S +bass/MS +basset/SM +bassinet/MS +bassist/MS +basso/MS +bassoon/MS +bassoonist/SM +basswood/MS +bast/M +bastard/MS +bastardization/MS +bastardize/GDS +bastardy/M +baste/ZGNXDRS +baster/M +bastion/M +bat/SM +batch/MDSG +bate/KACGSD +bath/ZGMDRS +bathe/M +bather/M +bathetic +bathhouse/MS +bathing/M +bathmat/MS +bathos/M +bathrobe/SM +bathroom/SM +baths +bathtub/MS +bathwater +bathyscaphe/SM +bathysphere/MS +batik/MS +batiste/M +batman/M +batmen +baton/MS +batsman/M +batsmen +battalion/SM +batted +batten/GSMD +batter/JZGSMDR +batterer/M +battery/SM +batting/M +battle/LDRSMZG +battleaxe/MS +battledore/SM +battledress +battlefield/MS +battlefront/MS +battleground/MS +battlement/SM +battler/M +battleship/SM +batty/RT +bauble/SM +baud/SM +bauxite/M +bawd/SM +bawdily +bawdiness/M +bawdy/PRT +bawl/SGMD +bay/SMDG +bayberry/SM +bayonet/SMDG +bayou/MS +bazaar/SM +bazillion/S +bazooka/SM +bbl +bdrm +be +beach/MDSG +beachcomber/SM +beachfront +beachhead/MS +beachwear/M +beacon/SM +bead/SGMD +beading/M +beadle/SM +beady/RT +beagle/SM +beak/SZMDR +beaker/M +beam/SGMD +bean/SGMD +beanbag/MS +beanfeast/S +beanie/SM +beanpole/MS +beansprout/S +beanstalk/MS +bear/SZGBJMR +bearable/U +bearably/U +beard/MDGS +beardless +bearer/M +bearing/M +bearish/PY +bearishness/M +bearlike +bearskin/MS +beast/MS +beastliness/M +beastly/TPRM +beat/SZGBMNRJ +beatable/U +beaten/U +beater/M +beatific +beatifically +beatification/M +beatify/GXNDS +beating/M +beatitude/SM +beatnik/MS +beau/SM +beaut/MS +beauteous/Y +beautician/SM +beautification/M +beautifier/M +beautiful/Y +beautify/NDRSZG +beauty/SM +beaver/SGMD +bebop/MS +becalm/GSD +became +because +beck/SM +beckon/SGD +becloud/GDS +become/S +becoming/UY +becquerel/S +bed/SM +bedaub/GSD +bedazzle/GDSL +bedazzlement/M +bedbug/SM +bedchamber/S +bedclothes/M +bedded +bedder +bedding/M +bedeck/GSD +bedevil/LGDS +bedevilment/M +bedfellow/SM +bedhead/S +bedim/S +bedimmed +bedimming +bedizen/GDS +bedlam/SM +bedpan/SM +bedpost/SM +bedraggle/GDS +bedridden +bedrock/SM +bedroll/SM +bedroom/SM +bedside/SM +bedsit/S +bedsitter/S +bedsore/SM +bedspread/SM +bedstead/SM +bedtime/SM +bee/RSMZGJ +beebread/M +beech/MS +beechnut/MS +beef/SGMD +beefburger/SM +beefcake/MS +beefiness/M +beefsteak/MS +beefy/RPT +beehive/MS +beekeeper/MS +beekeeping/M +beeline/MS +been +beep/SZGMDR +beeper/M +beer/M +beery/TR +beeswax/M +beet/SM +beetle/MGDS +beetroot/S +beeves +befall/SGN +befell +befit/S +befitted +befitting/Y +befog/S +befogged +befogging +before +beforehand +befoul/DGS +befriend/SGD +befuddle/GLDS +befuddlement/M +beg/S +began +begat +beget/S +begetter/S +begetting +beggar/MDYGS +beggary/M +begged +begging +begin/S +beginner/SM +beginning/MS +begone +begonia/SM +begot +begotten +begrime/DSG +begrudge/DSG +begrudging/Y +beguile/DRSZGL +beguilement/M +beguiler/M +beguiling/Y +beguine/SM +begum/MS +begun +behalf/M +behalves +behave/GDS +behavior/SM +behavioral/Y +behaviorism/M +behaviorist/MS +behead/DGS +beheld +behemoth/M +behemoths +behest/MS +behind/MS +behindhand +behold/NRZGS +beholder/M +behoove/DSG +beige/M +being/M +bejewel/SDG +belabor/SDG +belated/Y +belay/GDS +belch/GMDS +beleaguer/GSD +belfry/SM +belie/DS +belief/EUM +beliefs +believable/U +believably/U +believe/EDRSZG +believer/EUMS +believing/U +belittle/LDSG +belittlement/M +bell/SGMD +belladonna/M +bellboy/SM +belle/MS +belled/A +belletrist/MS +belletristic +bellhop/SM +bellicose +bellicosity/M +belligerence/M +belligerency/M +belligerent/MYS +belling/A +bellman/M +bellmen +bellow/MDGS +bellwether/MS +belly/GDSM +bellyache/MGDS +bellybutton/SM +bellyful/MS +belong/JDGS +belonging/M +beloved/SM +below +belt/SGMD +beltway/SM +beluga/MS +belying +bemire/GDS +bemoan/DGS +bemuse/LGDS +bemused/Y +bemusement/M +bench/GMDS +benchmark/MS +bend/BSZGMR +bender/M +bendy/TR +beneath +benedictine +benediction/SM +benedictory +benefaction/SM +benefactor/MS +benefactress/MS +benefice/SM +beneficence/M +beneficent/Y +beneficial/Y +beneficiary/SM +benefit/SMDG +benevolence/SM +benevolent/Y +benighted/Y +benign/Y +benignant +benignity/M +bent/SM +bentonite +bentwood/M +benumb/DSG +benzene/M +benzine/M +benzyl +bequeath/DG +bequeaths +bequest/MS +berate/GDS +bereave/DSLG +bereavement/MS +bereft +beret/MS +berg/SM +beriberi/M +berk/S +berkelium/M +berm/SM +berry/GDSM +berrylike +berserk +berth/GMD +berths +beryl/MS +beryllium/M +beseech/ZGRS +beseecher/M +beseeching/Y +beseem/DSG +beset/S +besetting +beside/S +besiege/ZGDRS +besieger/M +besmear/DSG +besmirch/GDS +besom/MS +besot/S +besotted +besotting +besought +bespangle/DSG +bespatter/GSD +bespeak/SG +bespectacled +bespoke +bespoken +best/SGMD +bestial/Y +bestiality/M +bestiary/SM +bestir/S +bestirred +bestirring +bestow/DGS +bestowal/SM +bestrew/SDG +bestrewn +bestridden +bestride/SG +bestrode +bestseller/MS +bestselling +bet/SM +beta/SM +betake/GS +betaken +betcha +betel/M +bethink/SG +bethought +betide/GDS +betimes +betoken/GDS +betook +betray/DRZGS +betrayal/SM +betrayer/M +betroth/DG +betrothal/SM +betrothed/M +betroths +better/MDGLS +betterment/M +betting +bettor/MS +between +betwixt +bevel/GMDS +beverage/SM +bevvy/S +bevy/SM +bewail/DGS +beware/GDS +bewhiskered +bewigged +bewilder/LSGD +bewildering/Y +bewilderment/M +bewitch/GLDS +bewitching/Y +bewitchment/M +bey/SM +beyond +bezel/MS +bf +bhaji +bi/SMRZ +biannual/Y +bias/GMDS +biased/U +biathlon/SM +bib/SM +bible/MS +biblical +bibliographer/MS +bibliographic +bibliographical/Y +bibliography/SM +bibliophile/SM +bibulous +bicameral +bicameralism/M +bicarb/MS +bicarbonate/MS +bicentenary/SM +bicentennial/SM +bicep/MS +biceps/M +bicker/MDRZGS +bickerer/M +biconcave +biconvex +bicuspid/MS +bicycle/DRSMZG +bicycler/M +bicyclist/SM +bid/SMG +biddable +bidden/U +bidder/MS +bidding/M +biddy/SM +bide/S +bidet/MS +bidirectional/Y +biennial/MYS +biennium/MS +bier/M +biff/SGD +bifocal/S +bifocals/M +bifurcate/XDSGN +bifurcation/M +big/P +bigamist/SM +bigamous +bigamy/M +bigger +biggest +biggie/MS +biggish +bighead/SM +bighearted/P +bigheartedness/M +bighorn/SM +bight/MS +bigmouth/M +bigmouths +bigness/M +bigot/MDS +bigotry/SM +bigwig/MS +bijou/M +bijoux +bike/DRSMZG +biker/M +bikini/MS +bilabial/MS +bilateral/Y +bilberry/S +bile/M +bilge/MS +bilingual/SMY +bilingualism/M +bilious/P +biliousness/M +bilirubin +bilk/SZGDR +bilker/M +bill/SBJGMD +billboard/MS +billet/GMDS +billfold/SM +billhook/S +billiard/S +billiards/M +billing/M +billingsgate/M +billion/MHS +billionaire/SM +billionth/M +billionths +billow/GMDS +billowy +billy/SM +billycan/S +bimbo/MS +bimetallic/SM +bimetallism/M +bimodal +bimonthly/SM +bin/SM +binary/SM +binaural +bind's +bind/AUGS +binder/MS +bindery/SM +binding/MS +bindweed/M +binge/MDS +bingo/M +binman +binmen +binnacle/SM +binned +binning +binocular/MS +binomial/SM +bio/SM +biochemical/SMY +biochemist/MS +biochemistry/M +biodegradability/M +biodegrade/DSGB +biodiversity/M +bioethics/M +biofeedback/M +biofilm/MS +biog +biographer/SM +biographic +biographical/Y +biography/SM +biol +biologic +biological/Y +biologist/MS +biology/M +biomarker/MS +biomass/M +biomedical +bionic/S +bionically +bionics/M +biophysical +biophysicist/MS +biophysics/M +biopic/MS +biopsy/GDSM +bioreactor/S +biorhythm/MS +biosensor/S +biosphere/SM +biosynthesis +biotech +biotechnological +biotechnology/M +biotin/M +bipartisan +bipartisanship/M +bipartite +biped/MS +bipedal +biplane/MS +bipolar +bipolarity/M +biracial +birch/GMDS +bird/SZGMDR +birdbath/M +birdbaths +birdbrain/SMD +birdcage/S +birder/M +birdhouse/MS +birdie/MDS +birdieing +birdlike +birdlime/M +birdseed/M +birdsong +birdwatcher/SM +birdying +biretta/SM +birth/ZGMDR +birthday/MS +birther/M +birthmark/MS +birthplace/MS +birthrate/MS +birthright/MS +births/A +birthstone/SM +biscuit/SM +bisect/DGS +bisection/MS +bisector/SM +bisexual/MYS +bisexuality/M +bishop/MS +bishopric/SM +bismuth/M +bison/M +bisque/M +bistro/MS +bit/CSMG +bitch/GMDS +bitchily +bitchiness/M +bitchy/PRT +bitcoin/SM +bite/RSMZ +biter/M +biting/Y +bitmap/S +bitten +bitter/PMRYTS +bittern/SM +bitterness/M +bitters/M +bittersweet/MS +bitty/TR +bitumen/M +bituminous +bivalent +bivalve/SM +bivouac/MS +bivouacked +bivouacking +biweekly/SM +biyearly +biz/M +bizarre/Y +bk +bl/DG +blab/SM +blabbed +blabber/DGS +blabbermouth/M +blabbermouths +blabbing +black/PXTGMDNRYS +blackamoor/MS +blackball/SGMD +blackberry/GSM +blackbird/SM +blackboard/MS +blackcurrant/S +blacken/DG +blackface +blackguard/SM +blackhead/MS +blacking/M +blackish +blackjack/MDGS +blackleg/S +blacklist/MDSG +blackmail/MDRSZG +blackmailer/M +blackness/M +blackout/SM +blacksmith/M +blacksmiths +blacksnake/SM +blackthorn/SM +blacktop/SM +blacktopped +blacktopping +bladder/MS +blade/MDS +blag/S +blagged +blagging +blah/M +blahs/M +blame/BMGDRS +blameless/YP +blamelessness/M +blameworthiness/M +blameworthy/P +blammo +blanch/GDS +blancmange/MS +bland/PTRY +blandish/DSLG +blandishment/SM +blandness/M +blank/TGPMDRYS +blanket/GMDS +blankness/M +blare/MGDS +blarney/SMDG +blase +blaspheme/ZGDRS +blasphemer/M +blasphemous/Y +blasphemy/SM +blast/ZGMDRS +blaster/M +blastoff/MS +blat/S +blatancy/SM +blatant/Y +blather/SMDG +blaze/MZGDRS +blazer/M +blazon/MDGS +bldg +bleach/MDRSZG +bleached/U +bleacher/M +bleak/TPRY +bleakness/M +blear +blearily +bleariness/M +bleary/PRT +bleat/GMDS +bleed/ZGRS +bleeder/M +bleeding/M +bleep/ZGMDRS +bleeper/M +blemish/GMDS +blemished/U +blench/DSG +blend/ZGMDRS +blender/M +bless/GDSJ +blessed/YP +blessedness/M +blessing/M +bletch +blew +blight/ZGMDRS +blimey +blimp/MS +blimpish +blind/PZTGMDRYS +blinder/M +blindfold/SMDG +blinding/Y +blindness/M +blindside/DSG +blini/MS +blink/ZGMDRS +blinker/MDG +blintz/MS +blintze/M +blip/SM +bliss/M +blissful/YP +blissfulness/M +blister/GMDS +blistering/Y +blistery +blithe/PYTR +blitheness/M +blither/G +blithesome +blitz/GMDS +blitzkrieg/MS +blivet/S +blizzard/SM +bloat/ZGDRS +bloatware +blob/SM +blobbed +blobbing +bloc/SM +block's +block/UGDS +blockade/MZGDRS +blockader/M +blockage/MS +blockbuster/SM +blockbusting/M +blockchain/MS +blocker/MS +blockhead/SM +blockhouse/MS +blog/SM +blogged +blogger/MS +blogging +bloke/MS +blokish +blond/PTMRS +blonde/MS +blondish +blondness/M +blood/GMDS +bloodbath/M +bloodbaths +bloodcurdling +bloodhound/SM +bloodily +bloodiness/M +bloodless/YP +bloodlessness/M +bloodletting/M +bloodline/SM +bloodmobile/MS +bloodshed/M +bloodshot +bloodstain/SMD +bloodstock/M +bloodstream/SM +bloodsucker/SM +bloodsucking +bloodthirstily +bloodthirstiness/M +bloodthirsty/RPT +bloody/PTGDRS +bloom/ZGMDRS +bloomer/M +bloop/ZGMDRS +blooper/M +blossom/GMDS +blossomy +blot/SM +blotch/GMDS +blotchy/TR +blotted +blotter/MS +blotting +blotto +blouse/MGDS +blow/SZGMR +blower/M +blowfly/SM +blowgun/MS +blowhard/MS +blowhole/S +blowjob/SM +blowlamp/S +blown +blowout/SM +blowpipe/SM +blowtorch/MS +blowup/MS +blowy/TR +blowzy/RT +blubber/GSMD +blubbery +bludgeon/MDGS +blue/DRSPMTG +bluebell/MS +blueberry/SM +bluebird/MS +bluebonnet/SM +bluebottle/SM +bluefish/MS +bluegill/MS +bluegrass/M +blueish +bluejacket/SM +bluejeans/M +blueness/M +bluenose/MS +bluepoint/MS +blueprint/MDGS +bluestocking/SM +bluesy/RT +bluet/MS +bluff/ZTGPMDRYS +bluffer/M +bluffness/M +bluing/M +bluish +blunder/MDRZGS +blunderbuss/MS +blunderer/M +blunt/PTGDRYS +bluntness/M +blur/SM +blurb/MS +blurred +blurriness/M +blurring +blurry/TRP +blurt/GDS +blush/ZGMDRS +blusher/M +bluster/MDRSZG +blusterer/M +blusterous +blustery +blvd +boa/SM +boar/SM +board/ZGMDRS +boarder/M +boarding/M +boardinghouse/MS +boardroom/MS +boardwalk/MS +boast/ZGMDRS +boaster/M +boastful/PY +boastfulness/M +boat/SZGMDR +boater/M +boathouse/MS +boating/M +boatload/S +boatman/M +boatmen +boatswain/SM +boatyard/S +bob/SM +bobbed +bobbin/MS +bobbing +bobble/MGDS +bobby/SM +bobbysoxer/SM +bobcat/MS +bobolink/SM +bobsled/SM +bobsledded +bobsledder/MS +bobsledding +bobsleigh/M +bobsleighs +bobtail/SM +bobwhite/MS +boccie/M +bock/M +bod/SMDG +bodacious +bode/S +bodega/MS +bodge/GDS +bodice/MS +bodily +bodkin/MS +body/DSM +bodybuilder/SM +bodybuilding/M +bodyguard/MS +bodysuit/SM +bodywork/M +boffin/S +boffo +bog/SM +boga +bogey/GMDS +bogeyman/M +bogeymen +bogged +bogging +boggle/GDS +boggy/TR +bogie/MS +bogon +bogosity +bogus +bogyman/M +bogymen +bohemian/SM +bohemianism/M +boil/SJZGMDR +boiler/M +boilermaker/SM +boilerplate/M +boink/GDS +boisterous/YP +boisterousness/M +bola/SM +bold/PTRY +boldface/DM +boldness/M +bole/SM +bolero/MS +bolivar/MS +bolivares +boll/SM +bollard/S +bollix/GMDS +bollocking/S +bollocks +bologna/M +bolshie +bolster/GMDS +bolt's +bolt/USGD +bolthole/S +bolus/MS +bomb/SJZGMDR +bombard/GDLS +bombardier/MS +bombardment/SM +bombast/M +bombastic +bombastically +bomber/M +bombproof +bombshell/SM +bombsite/S +bonanza/MS +bonbon/MS +bonce/S +bond/SGMD +bondage/M +bondholder/MS +bonding/M +bondman/M +bondmen +bondsman/M +bondsmen +bondwoman/M +bondwomen +bone/DRSMZG +bonehead/SMD +boneless +boner/M +boneshaker/S +boneyard +bonfire/MS +bong/SGMD +bongo/MS +bonhomie/M +boniness/M +bonito/MS +bonk/SZGD +bonnet/MS +bonny/TR +bonobo/MS +bonsai/M +bonus/MS +bony/PTR +boo/SMDHG +boob/SGMD +booby/SM +boodle/MS +booger/S +boogeyman/M +boogeymen +boogie/MDS +boogieing +boogieman/M +boohoo/GMDS +book/SBJGMD +bookbinder/SM +bookbindery/SM +bookbinding/M +bookcase/MS +bookend/MS +bookie/MS +booking/M +bookish +bookkeeper/MS +bookkeeping/M +booklet/MS +bookmaker/SM +bookmaking/M +bookmark/SMDG +bookmobile/SM +bookplate/MS +bookseller/MS +bookshelf/M +bookshelves +bookshop/SM +bookstall/S +bookstore/MS +bookworm/SM +boolean +boom/SZGMDR +boombox/MS +boomerang/MDGS +boon/SM +boondocks/M +boondoggle/MZGDRS +boondoggler/M +boonies/M +boor/SM +boorish/PY +boorishness/MS +boost/ZGMDRS +booster/M +boot's +boot/ASGD +bootblack/SM +bootee/MS +booth/M +booths +bootlace/S +bootleg/MS +bootlegged +bootlegger/MS +bootlegging/M +bootless +bootstrap/MS +bootstrapped +bootstrapping +booty/SM +booze/MZGDRS +boozer/M +boozy/TR +bop/SM +bopped +bopping +borax/M +bordello/MS +border/GMDS +borderland/MS +borderline/MS +bore/DRSMZG +boredom/M +borehole/S +borer/M +boring/Y +born/IAU +borne +boron/M +borough/M +boroughs +borrow/SDRZGJ +borrower/M +borrowing/M +borscht/M +borstal/S +borzoi/SM +bosh/M +bosom's +bosom/US +bosomy +boss/DSGM +bossily +bossiness/M +bossism/M +bossy/RTP +bot/S +botanic +botanical/Y +botanist/SM +botany/M +botch/DRSZGM +botcher/M +both +bother/SMDG +botheration +bothered/U +bothersome +botnet/SM +bottle/DRSMZG +bottleneck/MS +bottler/M +bottom/SMDG +bottomless +botulinum +botulism/M +boudoir/SM +bouffant/SM +bougainvillea/MS +bough/M +boughs +bought +bouillabaisse/SM +bouillon/MS +boulder/SM +boules +boulevard/SM +bounce/DRSMZG +bouncer/M +bouncily +bounciness/M +bouncy/RTP +bound/ASMGD +boundary/SM +bounden +bounder/SM +boundless/PY +boundlessness/M +bounteous/YP +bounteousness/M +bountiful/YP +bountifulness/M +bounty/SM +bouquet/SM +bourbon/SM +bourgeois/M +bourgeoisie/M +boustrophedon +bout/MS +boutique/SM +boutonniere/MS +bouzouki/MS +bovine/SM +bovver +bow/ZGSMDR +bowdlerization/MS +bowdlerize/DSG +bowed/U +bowel/SM +bower/M +bowl/MDRZGS +bowleg/SM +bowlegged +bowler/M +bowlful/SM +bowline/SM +bowling/M +bowman/M +bowmen +bowsprit/SM +bowstring/SM +bowwow/SM +box/ZGMDNRS +boxcar/SM +boxer/M +boxing/M +boxlike +boxroom/S +boxwood/M +boxy/RT +boy/SM +boycott/SGMD +boyfriend/MS +boyhood/SM +boyish/YP +boyishness/M +boysenberry/SM +bozo/MS +bpm +bps +bra/SM +brace/MZGDRS +bracelet/MS +bracer/M +bracero/MS +bracken/M +bracket/GMDS +brackish/P +brackishness/M +bract/MS +brad/SM +bradawl/S +bradycardia +brae/SM +brag/SM +braggadocio/SM +braggart/SM +bragged +bragger/MS +bragging +braid/GMDS +braiding/M +braille/M +brain/GMDS +brainchild/M +brainchildren/M +braininess/M +brainless/Y +brainpower +brainstorm/SMDG +brainstorming/M +brainteaser/SM +brainwash/DSG +brainwashing/M +brainwave/S +brainy/PTR +braise/GDS +brake/MGDS +brakeman/M +brakemen +bramble/MS +brambly +bran/M +branch/GMDS +branchlike +brand/ZGMDRS +branded/U +brander/M +brandish/DSG +brandy/GDSM +brash/PTRY +brashness/M +brass/MS +brasserie/MS +brassiere/MS +brassily +brassiness/M +brassy/PTR +brat/SM +bratty/RT +bratwurst/SM +bravado/M +brave/GPMYDTRS +braveness/M +bravery/M +bravo/SM +bravura/SM +brawl/SDRZGM +brawler/M +brawn/M +brawniness/M +brawny/RTP +bray/DGSM +braze/DRSZG +brazen/SDYGP +brazenness/M +brazer/M +brazier/SM +breach/GMDS +bread/GMDHS +breadbasket/SM +breadboard/SM +breadbox/MS +breadcrumb/MS +breadfruit/SM +breadline/MS +breadth/M +breadths +breadwinner/SM +break/BMZGRS +breakable/MS +breakage/MS +breakaway/MS +breakdown/MS +breaker/M +breakfast/MDGS +breakfront/MS +breakneck +breakout/MS +breakpoints +breakthrough/M +breakthroughs +breakup/SM +breakwater/SM +bream/MS +breast/SMDG +breastbone/MS +breastfed +breastfeed/GS +breastplate/SM +breaststroke/SM +breastwork/MS +breath/MDRSZGB +breathalyze/ZGDRS +breathe +breather/M +breathing/M +breathless/PY +breathlessness/M +breaths +breathtaking/Y +breathy/RT +bred/I +breech/MS +breed/SRZGM +breeder/M +breeding/IM +breeze/DSMG +breezeway/SM +breezily +breeziness/M +breezy/RTP +brethren +breve/SM +brevet/SM +brevetted +brevetting +breviary/SM +brevity/M +brew/MDRZGS +brewer/M +brewery/SM +brewpub/SM +bribe/DRSMZG +briber/M +bribery/M +brick/SMDG +brickbat/SM +brickie/S +bricklayer/MS +bricklaying/M +brickwork/M +brickyard/S +bridal/SM +bride/SM +bridegroom/SM +bridesmaid/MS +bridge/DSMG +bridgeable/U +bridgehead/SM +bridgework/M +bridle/DSMG +bridled/U +bridleway/S +brie/MZR +brief's +brief/CSDTGJ +briefcase/SM +briefer +briefing/CM +briefly +briefness/M +brier/M +brig/MS +brigade/SM +brigadier/MS +brigand/SM +brigandage/M +brigantine/MS +bright/SPNRYXT +brighten/DRZG +brightener/M +brightness/M +brights/M +brill +brilliance/M +brilliancy/M +brilliant/MYS +brilliantine/M +brim/MS +brimful +brimless +brimmed +brimming +brimstone/M +brindle/DM +brine/M +bring/SRZG +bringer/M +brininess/M +brink/SM +brinkmanship/M +briny/RTP +brioche/SM +briquette/MS +brisk/SDRYTGP +brisket/SM +briskness/M +bristle/DSMG +bristly/TR +britches/M +brittle/PRMT +brittleness/M +bro/SMH +broach/MDSG +broad/SMNRYXTP +broadband/M +broadcast/AMGS +broadcaster/MS +broadcasting/M +broadcloth/M +broaden/DG +broadloom/M +broadminded +broadness/M +broadsheet/SM +broadside/MGDS +broadsword/SM +brocade/DSMG +broccoli/M +brochette/SM +brochure/MS +brogan/SM +brogue/SM +broil/SMDRZG +broiler/M +broke +broken/YP +brokenhearted/Y +brokenness/M +broker/SMDG +brokerage/MS +brolly/S +bromide/SM +bromidic +bromine/M +bronc/SM +bronchi +bronchial +bronchitic +bronchitis/M +bronchus/M +bronco/SM +broncobuster/SM +brontosaur/MS +brontosaurus/MS +bronze/DSMG +brooch/MS +brood/SMDRZG +brooder/M +broodily +brooding/MY +broodmare/MS +broody/RMPT +brook/SMDG +brooklet/SM +broom/SM +broomstick/MS +broth/MRZ +brothel/MS +brother/MY +brotherhood/MS +brotherliness/M +broths +brougham/SM +brought +brouhaha/SM +brow/MS +browbeat/SNG +brown/SMDRPTG +brownfield +brownie/MS +brownish +brownness/M +brownout/SM +brownstone/MS +browse/DRSMZG +browser/M +brr +bruin/SM +bruise/DRSMZG +bruiser/M +bruising/M +bruit/SDG +brunch/MDSG +brunet/SM +brunette/MS +brunt/M +brush/MDSG +brushoff/SM +brushstroke/S +brushwood/M +brushwork/M +brusque/RPYT +brusqueness/M +brutal/Y +brutality/SM +brutalization/M +brutalize/GDS +brute/SM +brutish/PY +brutishness/M +bu +bub/SM +bubble/DSMG +bubblegum/M +bubbly/RMT +bubo/M +buboes +buccaneer/SGMD +buck/MDGS +buckaroo/SM +buckboard/MS +bucket/SGMD +bucketful/MS +buckeye/MS +buckle's +buckle/UDSG +buckler/MS +buckram/M +bucksaw/MS +buckshot/M +buckskin/MS +buckteeth +bucktooth/MD +buckwheat/M +buckyball/SM +bucolic/MS +bucolically +bud/SM +budded +budding/S +buddy/SM +budge/DSG +budgerigar/MS +budget/SGMD +budgetary +budgie/SM +buff/AMDGS +buffalo/MDG +buffaloes +buffer/SMDG +buffet/SMDGJ +buffoon/SM +buffoonery/M +buffoonish +bug's +bug/CS +bugaboo/SM +bugbear/SM +bugged/C +bugger/SMDG +buggery +bugging/C +buggy/RSMT +bugle/DRSMZG +bugler/M +build/SMRZGJ +builder/M +building/M +buildup/SM +built/AI +builtin +bulb/MS +bulbous +bulge/DSMG +bulgy/RT +bulimarexia/M +bulimia/M +bulimic/SM +bulk/MDGS +bulkhead/MS +bulkiness/M +bulky/RTP +bull/MDGS +bulldog/SM +bulldogged +bulldogging +bulldoze/ZGDRS +bulldozer/M +bullet/SMD +bulletin/MDGS +bulletproof/SDG +bullfight/SMRZG +bullfighter/M +bullfighting/M +bullfinch/MS +bullfrog/MS +bullhead/MDS +bullheaded/PY +bullheadedness/M +bullhorn/MS +bullion/M +bullish/YP +bullishness/M +bullock/SM +bullpen/SM +bullring/MS +bullseye +bullshit/MS! +bullshitted/! +bullshitter/SM! +bullshitting/! +bullwhip/S +bully/DSMG +bulrush/MS +bulwark/MS +bum/SM +bumbag/S +bumble/DRSZG +bumblebee/SM +bumbler/M +bumf +bummed +bummer/SM +bummest +bumming +bump/MDRZGS +bumper/M +bumph +bumpiness/M +bumpkin/MS +bumptious/PY +bumptiousness/M +bumpy/PRT +bun/SM +bunch/MDSG +bunchy/RT +bunco/SMDG +bundle/DSMG +bung/MDGS +bungalow/MS +bungee/SM +bunghole/MS +bungle/DRSMZG +bungler/M +bunion/SM +bunk's +bunk/CDGS +bunker/SM +bunkhouse/SM +bunkum/M +bunny/SM +bunt/MDGSJ +bunting/M +buoy/MDGS +buoyancy/M +buoyant/Y +bur/SMY +burble/DSMG +burbs/M +burden's +burden/USGD +burdensome +burdock/M +bureau/SM +bureaucracy/SM +bureaucrat/MS +bureaucratic +bureaucratically +bureaucratization/M +bureaucratize/GDS +burg/MRZS +burgeon/DSG +burger/M +burgh/MRZ +burgher/M +burghs +burglar/MS +burglarize/GDS +burglarproof +burglary/SM +burgle/DSG +burgomaster/SM +burgundy/SM +burial/ASM +burka/SM +burl/MDS +burlap/M +burlesque/MGDS +burliness/M +burly/RPT +burn/MDRZGSB +burnable/SM +burner/M +burnish/ZGMDRS +burnisher/M +burnoose/MS +burnout/MS +burnt +burp/MDGS +burr/MDGS +burrito/MS +burro/SM +burrow/SMDRZG +burrower/M +bursa/M +bursae +bursar/SM +bursary/SM +bursitis/M +burst/SMG +bury/ADSG +bus/AMS +busboy/SM +busby/SM +bused +busgirl/MS +bush/MDSGJ +bushel/SGMD +bushiness/M +bushing/M +bushman/M +bushmaster/SM +bushmen +bushwhack/DRSZG +bushwhacker/M +bushy/RPT +busily +business/MS +businesslike +businessman/M +businessmen +businessperson/SM +businesswoman/M +businesswomen +busing/M +busk/DRZGS +buskin/SM +busload/S +buss/MDSG +bust/MDRZGS +buster/M +bustle/DSMG +busty/RZT +busy/DRSTGP +busybody/SM +busyness/M +busywork/M +but/ACS +butane/M +butch/MRSZ +butcher/MDG +butchery/SM +butler/SM +butt/MDRZGS +butte/SM +butted/A +butter/MDG +butterball/MS +buttercream +buttercup/SM +butterfat/M +butterfingered +butterfingers/M +butterfly/GDSM +buttermilk/M +butternut/SM +butterscotch/M +buttery/TRSM +butting/A +buttock/SM +button's +button/USDG +buttonhole/DSMG +buttonwood/MS +buttress/MDSG +butty/S +buxom +buy/ZGSMR +buyback/SM +buyer/M +buyout/SM +buzz/MDRSZG +buzzard/MS +buzzer/M +buzzkill/SM +buzzword/SM +bx +bxs +by/M +bye/SM +bygone/SM +bylaw/SM +byline/SM +bypass/GMDS +bypath/M +bypaths +byplay/M +byproduct/MS +byre/S +byroad/SM +bystander/MS +byte/MS +byway/SM +byword/SM +byzantine +c/IES +ca +cab/SMRZ +cabal/MS +cabala's +caballero/MS +cabana/SM +cabaret/SM +cabbage/MS +cabbed +cabbing +cabby/SM +cabdriver/SM +cabin/MS +cabinet/SM +cabinetmaker/MS +cabinetmaking/M +cabinetry/M +cabinetwork/M +cable/MGDS +cablecast/GMS +cablegram/MS +cabochon/SM +caboodle/M +caboose/SM +cabriolet/SM +cabstand/SM +cacao/MS +cache/MGDS +cachepot/SM +cachet/MS +cackle/MZGDRS +cackler/M +cacophonous +cacophony/SM +cacti +cactus/M +cad/SM +cadaver/SM +cadaverous +caddie/MDS +caddish/YP +caddishness/M +caddying +cadence/DSM +cadenza/SM +cadet/MS +cadge/ZGDRS +cadger/M +cadmium/M +cadre/MS +caducei +caduceus/M +caesura/SM +cafe/SM +cafeteria/MS +cafetiere/S +caff/CS +caffeinated +caffeine/M +caftan/MS +cage/DSMG +cagey +cagier +cagiest +cagily +caginess/M +cagoule/S +cahoot/MS +caiman/MS +cairn/MS +caisson/SM +caitiff/SM +cajole/ZGLDRS +cajolement/M +cajoler/M +cajolery/M +cake/DSMG +cakewalk/SM +cal +calabash/MS +calaboose/SM +calamari/SM +calamine/M +calamitous/Y +calamity/SM +calcareous +calciferous +calcification/M +calcify/GNDS +calcimine/DSMG +calcine/DSG +calcite/M +calcium/M +calculable/I +calculate/AGNVDSX +calculated/Y +calculating/Y +calculation/AM +calculator/SM +calculi +calculus/M +caldera/SM +calendar/MDGS +calf/M +calfskin/M +caliber/SM +calibrate/GNDSX +calibration/M +calibrator/SM +calico/M +calicoes +californium/M +caliper/SGMD +caliph/M +caliphate/MS +caliphs +calisthenic/S +calisthenics/M +calk/SGMD +call/ASGMD +calla/MS +callable +callback/MS +called/U +caller/MS +calligrapher/SM +calligraphic +calligraphist/MS +calligraphy/M +calling/SM +calliope/MS +callosity/SM +callous/PGDSY +callousness/M +callow/RPT +callowness/M +callus/MDSG +calm/PSTGMDRY +calmness/M +caloric +calorie/MS +calorific +calumet/MS +calumniate/GNDS +calumniation/M +calumniator/MS +calumnious +calumny/SM +calve/GDS +calypso/MS +calyx/MS +cam/SM +camaraderie/M +camber/MDSG +cambial +cambium/SM +cambric/M +camcorder/SM +came +camel/MS +camelhair +camellia/MS +cameo/MS +camera/MS +cameraman/M +cameramen +camerapeople +cameraperson +camerawoman/M +camerawomen +camerawork +camiknickers +camisole/SM +camouflage/MZGDRS +camouflager/M +camp's +camp/CSGD +campaign/SMDRZG +campaigner/M +campanile/SM +campanologist/MS +campanology/M +camper/MS +campfire/SM +campground/SM +camphor/M +camping/M +campsite/SM +campus/MS +campy/TR +camshaft/SM +can't +can/SMDRZG +canal/MS +canalization/M +canalize/GDS +canape/MS +canard/MS +canary/SM +canasta/M +cancan/MS +cancel/DRSZG +canceler/M +cancellation/SM +cancelous +cancer/MS +cancerous +candelabra/SM +candelabrum/M +candid/YP +candida +candidacy/SM +candidate/MS +candidature/SM +candidness/M +candle/MZGDRS +candlelight/M +candlelit +candlepower/M +candler/M +candlestick/MS +candlewick/SM +candor/M +candy/GDSM +candyfloss +cane/SM +canebrake/MS +caner/M +canine/MS +canister/SM +canker/GMDS +cankerous +cannabis/MS +canned +cannelloni/M +cannery/SM +cannibal/SM +cannibalism/M +cannibalistic +cannibalization/M +cannibalize/GDS +cannily/U +canniness/M +canning +cannon/GMDS +cannonade/MGDS +cannonball/SM +cannot +canny/UTR +canoe/MDS +canoeing +canoeist/SM +canola/M +canon/MS +canonical/Y +canonization/SM +canonize/DSG +canoodle/DSG +canopy/GDSM +canst +cant's +cant/CZRDGS +cantabile +cantaloupe/SM +cantankerous/PY +cantankerousness/M +cantata/MS +canteen/MS +canter/CM +cantered +cantering +canticle/MS +cantilever/MDGS +canto/MS +canton/MLS +cantonal +cantonment/MS +cantor/MS +canvas/MGDS +canvasback/SM +canvass/MDRSZG +canvasser/M +canyon/MGS +cap/SMDRBZ +capabilities +capability/IM +capable/I +capably/I +capacious/PY +capaciousness/M +capacitance/M +capacities +capacitor/SM +capacity/IM +caparison/MDGS +cape/SM +caper/GMD +capeskin/M +capillarity/M +capillary/SM +capital/MSY +capitalism/M +capitalist/SM +capitalistic +capitalistically +capitalization/M +capitalize/ADSG +capitation/CSM +capitol/SM +capitulate/ADSXGN +capitulation/AM +caplet/MS +capo/SM +capon/MS +capped/UA +capping/UA +cappuccino/SM +caprice/SM +capricious/PY +capriciousness/M +capsicum/SM +capsize/DSG +capstan/SM +capstone/MS +capsular +capsule/DSMG +capsulize/DSG +capt +captain/SMDG +captaincy/SM +caption/SMDG +captious/YP +captiousness/M +captivate/DSGN +captivation/M +captivator/SM +captive/SM +captivity/SM +captor/MS +capture/ADSMG +car/SMDRZG +carafe/MS +caramel/SM +caramelize/DSG +carapace/SM +carat/MS +caravan/SM +caravansary/SM +caravel/SM +caraway/SM +carbide/SM +carbine/SM +carbohydrate/SM +carbolic +carbon/MS +carbonaceous +carbonate/MGNDS +carbonation/M +carboniferous +carbonize/GDS +carborundum/M +carboy/MS +carbs +carbuncle/SM +carbuncular +carburetor/SM +carcass/MS +carcinogen/SM +carcinogenic/MS +carcinogenicity/M +carcinoma/MS +card/ESGMD +cardamom/SM +cardamon/S +cardboard/M +carder/MS +cardholder/S +cardiac +cardie/S +cardigan/SM +cardinal/SMY +cardio +cardiogram/SM +cardiograph/M +cardiographs +cardiologist/MS +cardiology/M +cardiomyopathy +cardiopulmonary +cardiovascular +cardsharp/MRZS +cardsharper/M +care/SM +careen/DGS +career/MDGS +careerism +careerist/SM +carefree +careful/YP +carefuller +carefullest +carefulness/M +caregiver/SM +careless/PY +carelessness/M +carer/M +caress/MDSG +caret/MS +caretaker/MS +careworn +carfare/M +cargo/M +cargoes +carhop/MS +caribou/SM +caricature/MGDS +caricaturist/SM +caries/M +carillon/SM +caring/M +carious +carjack/JSDRZG +carjacker/M +carjacking/M +carload/SM +carmaker/S +carmine/SM +carnage/M +carnal/Y +carnality/M +carnation/IMS +carnelian/MS +carnival/MS +carnivora +carnivore/SM +carnivorous/PY +carnivorousness/M +carny/SM +carob/MS +carol/ZGMDRS +caroler/M +carom/GMDS +carotene/M +carotid/SM +carousal/SM +carouse/DRSMZG +carousel/SM +carouser/M +carp/SZGMDR +carpal/MS +carpel/MS +carpenter/MDGS +carpentry/M +carper/M +carpet/MDGS +carpetbag/MS +carpetbagged +carpetbagger/MS +carpetbagging +carpeting/M +carpi +carpool/SMDG +carport/SM +carpus/M +carrel/MS +carriage/SM +carriageway/S +carrier/M +carrion/M +carrot/MS +carroty +carry/ZGDRSM +carryall/SM +carrycot/S +carryout +carryover/MS +carsick/P +carsickness/M +cart/SZGMDR +cartage/M +cartel/MS +carter/M +carthorse/SM +cartilage/SM +cartilaginous +cartload/SM +cartographer/SM +cartographic +cartography/M +carton/MS +cartoon/SMDG +cartoonist/MS +cartridge/MS +cartwheel/GMDS +carve/JZGDRS +carver/M +carvery/S +carving/M +caryatid/MS +casaba/MS +cascade/DSMG +cascara/SM +case/LDSJMG +casebook/S +cased/U +caseharden/DGS +casein/M +caseload/MS +casement/MS +casework/ZMR +caseworker/M +cash/GMDS +cashback/M +cashbook/MS +cashew/MS +cashier/GSMD +cashless +cashmere/M +casing/M +casino/MS +cask/SM +casket/MS +cassava/SM +casserole/DSMG +cassette/MS +cassia/MS +cassock/SM +cassowary/SM +cast/ASGM +castanet/MS +castaway/MS +caste/JMZRS +castellated +caster/M +castigate/DSGN +castigation/M +castigator/SM +casting/AM +castle/MGDS +castoff/SM +castor/MS +castrate/GNXDS +castration/M +casual/PMYS +casualness/M +casualty/SM +casuist/SM +casuistic +casuistry/M +cat/SM +cataclysm/MS +cataclysmal +cataclysmic +catacomb/SM +catafalque/MS +catalepsy/M +cataleptic/MS +catalog/ZGSMDR +cataloger/M +catalpa/SM +catalyses +catalysis/M +catalyst/MS +catalytic/M +catalyze/GDS +catamaran/SM +catapult/GMDS +cataract/MS +catarrh/M +catastrophe/MS +catastrophic +catastrophically +catatonia/M +catatonic/SM +catbird/SM +catboat/SM +catcall/GSMD +catch/ZGJLMRS +catchall/MS +catcher/M +catchment/MS +catchpenny +catchphrase/SM +catchword/MS +catchy/RT +catechism/SM +catechist/SM +catechize/DSG +categorical/Y +categorization/MS +categorize/GDS +category/SM +cater/ZGJDRS +catercorner +caterer/M +caterpillar/MS +caterwaul/SMDG +catfish/MS +catgut/M +catharses +catharsis/M +cathartic/SM +cathedral/SM +catheter/SM +catheterize/DSG +cathode/SM +cathodic +catholic +catholicity/M +cation/MS +catkin/MS +catlike +catnap/MS +catnapped +catnapping +catnip/M +catsuit/S +cattail/SM +catted +cattery/S +cattily +cattiness/M +catting +cattle/M +cattleman/M +cattlemen +catty/TPR +catwalk/SM +caucus/MDSG +caudal/Y +caught/U +cauldron/MS +cauliflower/SM +caulk/ZGMDRS +caulker/M +causal/Y +causality/SM +causation/M +causative +cause/MZGDRS +causeless +causer/M +causerie/SM +causeway/SM +caustic/SM +caustically +causticity/M +cauterization/M +cauterize/GDS +caution/SMDG +cautionary +cautious/IY +cautiousness/M +cavalcade/MS +cavalier/SMY +cavalry/SM +cavalryman/M +cavalrymen +cave/DRSMZG +caveat/MS +caveman/M +cavemen +cavern/MS +cavernous/Y +caviar/M +cavil/ZGJMDRS +caviler/M +caving/M +cavitation +cavity/FSM +cavort/DGS +caw/SMDG +cay/CSM +cayenne/M +cayuse/MS +cc +cease/CMGDS +ceasefire/MS +ceaseless/YP +ceaselessness/M +ceca +cecal +cecum/M +cedar/MS +cede/FAGSD +ceder/MS +cedilla/SM +ceilidh +ceilidhs +ceiling/MS +celandine/M +celeb/S +celebrant/SM +celebrate/DSGNX +celebration/M +celebrator/SM +celebratory +celebrity/SM +celeriac +celerity/M +celery/M +celesta/MS +celestial/Y +celibacy/M +celibate/MS +cell/SMD +cellar/MS +cellist/SM +cellmate/SM +cello/MS +cellophane/M +cellphone/MS +cellular/SM +cellulite/M +cellulitis +celluloid/M +cellulose/M +cement/MDRZGS +cementer/M +cementum/M +cemetery/SM +cenobite/MS +cenobitic +cenotaph/M +cenotaphs +censer/MS +censor/MDGS +censored/U +censorial +censorious/PY +censoriousness/M +censorship/M +censure/BDRSMZG +censurer/M +census/MDSG +cent/SZMR +centaur/SM +centavo/SM +centenarian/MS +centenary/SM +centennial/MYS +center/MDG +centerboard/SM +centerfold/MS +centerpiece/MS +centigrade +centigram/SM +centiliter/MS +centime/SM +centimeter/MS +centipede/SM +central/SMY +centralism +centralist +centrality/M +centralization/CM +centralize/CGDS +centralizer/MS +centrifugal/Y +centrifuge/DSMG +centripetal/Y +centrism/M +centrist/MS +centurion/SM +century/SM +cephalic +ceramic/SM +ceramicist/SM +ceramics/M +ceramist/MS +cereal/MS +cerebellar +cerebellum/SM +cerebra +cerebral +cerebrate/GNDS +cerebration/M +cerebrovascular +cerebrum/MS +cerement/MS +ceremonial/SMY +ceremonious/UY +ceremoniousness/M +ceremony/SM +cerise/M +cerium/M +cermet/M +cert/S +certain/UY +certainty/USM +certifiable +certifiably +certificate/MGNXDS +certification/M +certify/DSG +certitude/IM +certitudes +cerulean/M +cervical +cervices +cervix/M +cesarean/MS +cesium/M +cessation/MS +cession/KAFSM +cesspit/S +cesspool/MS +cetacean/MS +ceteris +cf +cg +ch/IFVT +chad/S +chafe/GDS +chaff/GMDS +chaffinch/MS +chagrin/GSMD +chain's +chain/UGDS +chainsaw/MDGS +chair/GMDS +chairlift/MS +chairman/M +chairmanship/SM +chairmen +chairperson/SM +chairwoman/M +chairwomen +chaise/MS +chalcedony/M +chalet/MS +chalice/SM +chalk/GMDS +chalkboard/SM +chalkiness/M +chalky/PRT +challenge/DRSMZG +challenged/U +challenger/M +challis/M +chamber/SMD +chamberlain/MS +chambermaid/MS +chambray/M +chameleon/SM +chamois/M +chamomile/MS +champ/ZGMDS +champagne/MS +champion/GMDS +championship/MS +chance/MGDS +chancel/SM +chancellery/SM +chancellor/MS +chancellorship/M +chancery/SM +chanciness/M +chancre/SM +chancy/PRT +chandelier/SM +chandler/MS +change/MZGDRS +changeability/M +changeable/P +changeableness/M +changeably +changed/U +changeless/Y +changeling/SM +changeover/SM +changer/M +changing/U +channel/GSMD +channelization/M +channelize/DSG +chanson/SM +chant/ZGMDRS +chanter/M +chanteuse/MS +chantey/SM +chanticleer/MS +chaos/M +chaotic +chaotically +chap/SM +chaparral/SM +chapati/S +chapatti/S +chapbook/MS +chapeau/SM +chapel/MS +chaperon/MDGS +chaperonage/M +chaperoned/U +chaplain/MS +chaplaincy/SM +chaplet/SM +chapped +chapping +chappy/S +chapter/SM +char/SM +charabanc/MS +character/MS +characterful +characteristic/SM +characteristically/U +characterization/MS +characterize/DSG +characterless +charade/SM +charbroil/GDS +charcoal/MS +chard/M +chardonnay/SM +charge/AESDGM +chargeable/A +charged/U +charger/SM +charily +chariness/M +chariot/SM +charioteer/MS +charisma/M +charismatic/MS +charitable/P +charitableness/M +charitably/U +charity/SM +charlady/S +charlatan/SM +charlatanism/M +charlatanry/M +charlie/S +charm/ZGMDRS +charmer/M +charming/Y +charmless +charred +charring +chart/GMDS +charted/U +charter's +charter/ASGD +charterer/MS +chartreuse/M +charwoman/M +charwomen +chary/TRP +chase/MZGDRS +chaser/M +chasm/MS +chassis/M +chaste/PYTR +chasten/DGS +chasteness/M +chastise/DRSZGL +chastisement/SM +chastiser/M +chastity/M +chasuble/SM +chat/SM +chateau/SM +chateaux +chatelaine/SM +chatline/S +chatroom/M +chatted +chattel/MS +chatter/MDRZGS +chatterbox/MS +chatterer/M +chattily +chattiness/M +chatting +chatty/TPR +chauffeur/GMDS +chauvinism/M +chauvinist/SM +chauvinistic +chauvinistically +cheap/PXTNRY +cheapen/DG +cheapness/M +cheapo +cheapskate/MS +cheat/ZGMDRS +cheater/M +check/AGMDS +checkbook/SM +checkbox +checked/U +checker/MDGS +checkerboard/SM +checkers/M +checklist/MS +checkmate/MGDS +checkoff/SM +checkout/SM +checkpoint/SM +checkroom/MS +checksum +checkup/MS +cheddar/M +cheek/GMDS +cheekbone/SM +cheekily +cheekiness/M +cheeky/TPR +cheep/GMDS +cheer/ZGMDRS +cheerer/M +cheerful/YP +cheerfuller +cheerfullest +cheerfulness/M +cheerily +cheeriness/M +cheerio/MS +cheerleader/SM +cheerless/PY +cheerlessness/M +cheery/TPR +cheese/MGDS +cheeseboard/S +cheeseburger/SM +cheesecake/SM +cheesecloth/M +cheeseparing/M +cheesiness/M +cheesy/TPR +cheetah/M +cheetahs +chef/SM +chem +chemical/SMY +chemise/MS +chemist/MS +chemistry/M +chemo/M +chemotherapeutic +chemotherapy/M +chemurgy/M +chenille/M +cherish/DSG +cheroot/MS +cherry/SM +chert/M +cherub/MS +cherubic +cherubim +chervil/M +chess/M +chessboard/MS +chessman/M +chessmen +chest/MDS +chesterfield/SM +chestful/SM +chestnut/SM +chesty/TR +chevalier/SM +cheviot/M +chevron/MS +chew/SZGMDR +chewer/M +chewiness/M +chewy/PTR +chg +chge +chi/SM +chiaroscuro/M +chic/PTMR +chicane/MS +chicanery/SM +chichi/MS +chick/XMNS +chickadee/SM +chicken/MDG +chickenfeed/M +chickenhearted +chickenpox/M +chickenshit/S! +chickpea/SM +chickweed/M +chicle/M +chicness/M +chicory/SM +chide/GDS +chiding/Y +chief/TMRYS +chiefdom/M +chieftain/MS +chieftainship/SM +chiffon/M +chiffonier/MS +chigger/MS +chignon/MS +chihuahua/SM +chilblain/SM +child/M +childbearing/M +childbirth/M +childbirths +childcare/M +childhood/SM +childish/YP +childishness/M +childless/P +childlessness/M +childlike +childminder/S +childminding +childproof/GSD +children/M +chili/M +chilies +chill/JPZTGMDRS +chiller/M +chilliness/M +chilling/Y +chillness/M +chilly/TPR +chime/MZGDRS +chimer/M +chimera/MS +chimeric +chimerical +chimney/MS +chimp/MS +chimpanzee/SM +chin/SM +china/M +chinaware/M +chinchilla/MS +chine/MS +chink/GMDS +chinless +chinned +chinning +chino/MS +chinstrap/MS +chintz/M +chintzy/RT +chinwag/S +chip/SM +chipboard +chipmunk/SM +chipolata/S +chipped +chipper/MS +chippie +chipping/S +chippy/S +chirography/M +chiropodist/MS +chiropody/M +chiropractic/SM +chiropractor/SM +chirp/GMDS +chirpily +chirpy/PTR +chirrup/GMDS +chisel/ZGMDRS +chiseler/M +chit/SM +chitchat/SM +chitchatted +chitchatting +chitin/M +chitinous +chitosan +chitterlings/M +chivalrous/PY +chivalrousness/M +chivalry/M +chive/MS +chivy/GDS +chlamydia/MS +chlamydiae +chloral/M +chlordane/M +chloride/MS +chlorinate/GNDS +chlorination/M +chlorine/M +chlorofluorocarbon/SM +chloroform/SGMD +chlorophyll/M +chloroplast/MS +chm +choc/S +chock/GMDS +chockablock +chocoholic/SM +chocolate/MS +chocolaty +choice/MTRS +choir/MS +choirboy/MS +choirmaster/SM +choke/MZGDRS +chokecherry/SM +choker/M +cholecystectomy +cholecystitis +choler/M +cholera/M +choleric +cholesterol/M +chomp/ZGMDRS +choose/ZGRS +chooser/M +choosiness/M +choosy/TPR +chop/SM +chophouse/SM +chopped +chopper/MDGS +choppily +choppiness/M +chopping +choppy/TPR +chopstick/SM +choral/MYS +chorale/MS +chord/MS +chordal +chordate/SM +chore/MS +chorea/M +choreograph/DRZG +choreographer/M +choreographic +choreographically +choreographs +choreography/M +chorister/SM +choroid/MS +chortle/MZGDRS +chortler/M +chorus/GMDS +chose +chosen +chow/SGMD +chowder/MS +chrism/M +christen/ASGD +christening/MS +christian/U +christology +chromatic +chromatically +chromatin/M +chromatography +chrome/MGDS +chromium/M +chromosomal +chromosome/MS +chronic +chronically +chronicle/DRSMZG +chronicler/M +chronograph/M +chronographs +chronological/Y +chronologist/MS +chronology/SM +chronometer/SM +chrysalis/MS +chrysanthemum/MS +chub/SM +chubbiness/M +chubby/TPR +chuck/GMDS +chuckhole/SM +chuckle/MGDS +chuffed +chug/SM +chugged +chugging +chukka/MS +chum/SM +chummed +chummily +chumminess/M +chumming +chummy/PTR +chump/MS +chunder/GDS +chunk/GMDS +chunkiness/M +chunky/PTR +chunter/DGS +church/MS +churchgoer/SM +churchgoing/M +churchman/M +churchmen +churchwarden/MS +churchwoman +churchwomen +churchyard/SM +churl/MS +churlish/PY +churlishness/M +churn/ZGMDRS +churner/M +chute/MS +chutney/MS +chutzpah/M +chyme/M +chyron/MS +ciabatta/SM +ciao/S +cicada/MS +cicatrices +cicatrix/M +cicerone/SM +ciceroni +cider's +cider/S +cigar/MS +cigarette/MS +cigarillo/MS +cilantro/M +cilia +cilium/M +cinch/GMDS +cinchona/SM +cincture/SM +cinder/GMDS +cine +cinema/MS +cinematic +cinematographer/MS +cinematographic +cinematography/M +cinnabar/M +cinnamon/M +cipher's +cipher/CGDS +cir +circa +circadian +circle/MGDS +circlet/MS +circuit/MDGS +circuital +circuitous/YP +circuitousness/M +circuitry/M +circuity/M +circular/SMY +circularity/M +circularize/DSG +circulate/ADSG +circulation/SM +circulatory +circumcise/XDSGN +circumcised/U +circumcision/M +circumference/MS +circumferential +circumflex/MS +circumlocution/MS +circumlocutory +circumnavigate/XGNDS +circumnavigation/M +circumpolar +circumscribe/GDS +circumscription/MS +circumspect/Y +circumspection/M +circumstance/MGDS +circumstantial/Y +circumvent/DSG +circumvention/M +circus/MS +cirque/MS +cirrhosis/M +cirrhotic/SM +cirri +cirrus/M +cis +cisgender +cistern/MS +cit +citadel/MS +citation/AMS +cite's +cite/IAGSD +citified +citizen/MS +citizenry/M +citizenship/M +citric +citron/MS +citronella/M +citrus/MS +city/SM +citywide +civet/MS +civic/S +civically +civics/M +civil/UY +civilian/MS +civility/ISM +civilization/MS +civilize/GDS +civilized/U +civvies/M +ck +cl +clack/GMDS +clad/U +cladding/M +clade +claim's +claim/CKEAGDS +claimable/A +claimant/MS +claimed/U +claimer/ECSM +clairvoyance/M +clairvoyant/MS +clam/SM +clambake/MS +clamber/ZGMDRS +clamberer/M +clammed +clammily +clamminess/M +clamming +clammy/PTR +clamor/GMDS +clamorous +clamp/GMDS +clampdown/MS +clan/SM +clandestine/Y +clang/ZGMDRS +clangor/M +clangorous/Y +clank/GMDS +clannish/P +clannishness/M +clansman/M +clansmen +clanswoman +clanswomen +clap/SM +clapboard/MDGS +clapped +clapper/MS +clapperboard/S +clapping/M +claptrap/M +claque/MS +claret/MS +clarification/M +clarify/XDSNG +clarinet/SM +clarinetist/SM +clarion/MDGS +clarity/M +clash/GMDS +clasp's +clasp/UGDS +class/GMDS +classic/MS +classical/MY +classicism/M +classicist/MS +classifiable +classification/CAM +classifications +classified's +classified/U +classifieds +classifier/MS +classify/ACSDGN +classiness/M +classism +classless/P +classmate/MS +classroom/MS +classwork/M +classy/TRP +clatter/GMDS +clausal +clause/MS +claustrophobia/M +claustrophobic +clavichord/SM +clavicle/MS +clavier/MS +claw's +claw/CSGD +clay/M +clayey +clayier +clayiest +clean/BJPZTGDRYS +cleaner/M +cleaning/M +cleanliness/UM +cleanly/UTPR +cleanness/UM +cleanse/ZGDRS +cleanser/M +cleanup/MS +clear/JPTGMDRYS +clearance/SM +clearheaded +clearing/M +clearinghouse/SM +clearness/M +clearway/S +cleat/MS +cleavage/MS +cleave/ZGDRS +cleaver/M +clef/SM +cleft/MS +clematis/MS +clemency/IM +clement/Y +clementine/S +clench/GMDS +clerestory/SM +clergy/SM +clergyman/M +clergymen +clergywoman/M +clergywomen +cleric/MS +clerical/Y +clericalism/M +clerk/GMDS +clerkship/M +clever/PTRY +cleverness/M +clevis/MS +clew/SGMD +cliche/MDS +click/BZGMDRS +clickbait +clicker/M +client/MS +clientele/MS +cliff/MS +cliffhanger/SM +cliffhanging +clifftop/S +clii +climacteric/M +climactic +climate/SM +climatic +climatically +climatologist/SM +climatology/M +climax/MDSG +climb/SMDRZGB +climber/M +climbing/M +clime/SM +clinch/MDRSZG +clincher/M +cling/SMRZG +clinger/M +clingfilm +clingy/RT +clinic/SM +clinical/Y +clinician/SM +clink/SMDRZG +clinker/M +cliometric/S +cliometrician/MS +cliometrics/M +clip/SM +clipboard/MS +clipped +clipper/SM +clipping/SM +clique/SM +cliquey +cliquish/YP +cliquishness/M +clit/SM +clitoral +clitorides +clitoris/MS +clix +cloaca/M +cloacae +cloak's +cloak/USDG +cloakroom/MS +clobber/SMDG +cloche/SM +clock/SMDG +clockwise +clockwork/SM +clod/MS +cloddish +clodhopper/MS +clog's +clog/US +clogged/U +clogging/U +cloisonne/M +cloister/SMDG +cloistral +clomp/SDG +clonal +clone/DSMG +clonidine +clonk/SMDG +clop/MS +clopped +clopping +close/DRSMYTGJP +closefisted +closemouthed +closeness/M +closeout/MS +closet/SMDG +closeup/SM +closing/M +closure/ESM +clot/MS +cloth/M +clothe/UDSG +clotheshorse/MS +clothesline/SM +clothespin/SM +clothier/MS +clothing/M +cloths +clotted +clotting +cloture/SM +cloud/SMDG +cloudburst/SM +clouded/U +cloudiness/M +cloudless +cloudy/RPT +clout/SMDG +clove/RSMZ +cloven +clover/M +cloverleaf/SM +cloverleaves +clown/SMDG +clownish/YP +clownishness/M +cloy/DGS +cloying/Y +club/MS +clubbable +clubbed +clubber/S +clubbing +clubfeet +clubfoot/MD +clubhouse/SM +clubland +cluck/SMDG +clue/MGDS +clueless +clump/SMDG +clumpy/TR +clumsily +clumsiness/M +clumsy/TRP +clung +clunk/SMDRZG +clunker/M +clunky/TR +cluster/MDSG +clutch/GMDS +clutter's +clutter/UDSG +clvi +clvii +clxi +clxii +clxiv +clxix +clxvi +clxvii +cm +cnidarian/MS +co/ESD +coach/MDSG +coachload/S +coachman/M +coachmen +coachwork +coadjutor/MS +coagulant/MS +coagulate/GNDS +coagulation/M +coagulator/MS +coal/MDGS +coalesce/GDS +coalescence/M +coalescent +coalface/MS +coalfield/S +coalition/MS +coalitionist/MS +coalmine/S +coarse/RYTP +coarsen/SDG +coarseness/M +coast/SMDRZG +coastal +coaster/M +coastguard/S +coastline/MS +coat/MDGJS +coating/M +coatroom/S +coattail/SM +coauthor/MDGS +coax/DRSZG +coaxer/M +coaxial +coaxing/Y +cob/SM +cobalt/M +cobber/S +cobble/DRSMZG +cobbler/M +cobblestone/SM +cobnut/S +cobra/SM +cobweb/SM +cobwebbed +cobwebby/RT +coca/M +cocaine/M +cocci/S +coccus/M +coccyges +coccyx/M +cochineal/M +cochlea/SM +cochleae +cochlear +cock/MDGS +cockade/SM +cockamamie +cockatiel/MS +cockatoo/SM +cockatrice/SM +cockchafer/S +cockcrow/SM +cockerel/SM +cockeyed +cockfight/MGS +cockfighting/M +cockily +cockiness/M +cockle/SM +cockleshell/SM +cockney/SM +cockpit/SM +cockroach/MS +cockscomb/SM +cocksucker/MS! +cocksure +cocktail/MS +cocky/RTP +coco/MS +cocoa/SM +coconut/SM +cocoon/SMDG +cod/SM +coda/MS +codded +codding +coddle/DSG +code's +code/CZGDRS +codeine/M +codependency/M +codependent/SM +coder/CM +codex/M +codfish/MS +codger/SM +codices +codicil/SM +codification/M +codifier/M +codify/XDRSNZG +codon/S +codpiece/MS +codswallop +coed/MS +coeducation/M +coeducational +coefficient/MS +coelenterate/MS +coenzyme +coequal/MYS +coerce/DRSZGNV +coercer/M +coercion/M +coeval/SMY +coexist/DSG +coexistence/M +coexistent +coextensive +coffee/SM +coffeecake/SM +coffeehouse/MS +coffeemaker/SM +coffeepot/MS +coffer/SM +cofferdam/MS +coffin/SMDG +cog/SM +cogency/M +cogent/Y +cogitate/DSXGNV +cogitation/M +cogitator/MS +cognac/SM +cognate/MS +cognition/AM +cognitional +cognitive/Y +cognizable +cognizance/AM +cognizant +cognomen/SM +cognoscente/M +cognoscenti +cogwheel/SM +cohabit/SGD +cohabitant/MS +cohabitation/M +coheir/SM +cohere/DSG +coherence/IM +coherency/M +coherent/IY +cohesion/M +cohesive/YP +cohesiveness/M +coho/MS +cohort/SM +coif/MS +coiffed +coiffing +coiffure/DSMG +coil's/A +coil/UADGS +coin/MDRZGS +coinage/SM +coincide/DSG +coincidence/MS +coincident +coincidental/Y +coiner/M +coinsurance/M +coir +coital +coitus/M +coke/MGDS +col/S +cola/MS +colander/SM +cold/MRYTPS +coldblooded +coldness/M +coleslaw/M +coleus/MS +coley/S +colic/M +colicky +coliseum/MS +colitis/M +coll +collaborate/DSXGNV +collaboration/M +collaborationist +collaborative/Y +collaborator/MS +collage/SM +collagen +collapse/MGDS +collapsible +collar/SMDG +collarbone/SM +collard/SM +collarless +collate/DSXGN +collateral/MY +collateralize +collation/M +collator/MS +colleague/MS +collect's +collect/ASGVD +collected/U +collectedly +collectible/SM +collection/AMS +collective/MYS +collectivism/M +collectivist/SM +collectivization/M +collectivize/DSG +collector/MS +colleen/SM +college/SM +collegiality/M +collegian/MS +collegiate +collide/DRSZG +collie/RSMZ +collier/M +colliery/SM +collision/SM +collocate/MGNDSX +collocation/M +colloid/SM +colloidal +colloq +colloquial/Y +colloquialism/SM +colloquies +colloquium/MS +colloquy/M +collude/DSG +collusion/M +collusive +cologne/SM +colon/SM +colonel/SM +colonelcy/M +colones +colonial/SMY +colonialism/M +colonialist/MS +colonist/SM +colonization/ACM +colonize/CAGSD +colonizer/MS +colonnade/MDS +colonoscopy/SM +colony/SM +colophon/SM +color's +color/AEGDS +colorant/SM +coloration/EM +coloratura/MS +colorblind/P +colorblindness/M +colored's +colored/U +coloreds +colorfast/P +colorfastness/M +colorful/PY +colorfulness/M +coloring's +colorist/S +colorization/M +colorize/DSG +colorless/PY +colorlessness/M +colorway/S +colossal/Y +colossi +colossus/M +colostomy/SM +colostrum/M +colt/MS +coltish +columbine/SM +column/SMD +columnar +columnist/SM +com/JL +coma/MS +comaker/SM +comatose +comb/MDRZGJS +combat/SMDGV +combatant/SM +combativeness/M +combed/U +comber/M +combination/SM +combine's +combine/ADSG +combined/U +combiner/MS +combings/M +combo/SM +combust/SGVD +combustibility/M +combustible/MS +combustion/M +come/IMZGRS +comeback/MS +comedian/MS +comedic +comedienne/MS +comedown/MS +comedy/SM +comeliness/M +comely/RPT +comer's +comestible/SM +comet/SM +comeuppance/SM +comfit's +comfit/ES +comfort/ESMDG +comfortable/P +comfortableness/M +comfortably/U +comforter/MS +comforting/Y +comfortless +comfy/RT +comic/SM +comical/Y +comicality/M +coming/M +comity/M +comm +comma/SM +command/SMDRLZG +commandant/MS +commandeer/GDS +commander/M +commandment/MS +commando/SM +commemorate/XGNVDS +commemoration/M +commemorator/MS +commence/ADSLG +commencement/AM +commencements +commend/ASDBG +commendably +commendation/AMS +commendatory +commensurable +commensurate/IY +comment/GSMD +commentary/SM +commentate/DSG +commentator/SM +commerce/M +commercial/SMY +commercialism/M +commercialization/M +commercialize/GDS +commie/SM +commingle/DSG +commiserate/GNVDSX +commiseration/M +commissar/SM +commissariat/SM +commissary/SM +commission's +commission/ACSGD +commissionaire/S +commissioner/SM +commit/AS +commitment/MS +committal/SM +committed/AU +committee/SM +committeeman/M +committeemen +committeewoman/M +committeewomen +committer/S +committing/A +commode's +commode/EIS +commodification +commodious/Y +commodity/SM +commodore/SM +common's +common/UPRYT +commonality/S +commonalty/M +commoner/MS +commonness/UM +commonplace/MS +commons +commonsense +commonweal/MH +commonwealth/M +commonwealths +commotion/SM +communal/Y +commune/XDSMGN +communicability/M +communicable/I +communicably +communicant/MS +communicate/GNVDSX +communication/M +communicative/U +communicator/SM +communion/M +communique/SM +communism/M +communist/SM +communistic +community/SM +commutation/MS +commutative +commutativity +commutator/SM +commute/BDRSMZG +commuter/M +comorbidity +comp/MDYGS +compact/TGSMDRYP +compaction +compactness/M +compactor/SM +companion/SBM +companionably +companionship/M +companionway/MS +company/SM +comparability/M +comparable/I +comparably/I +comparative/MYS +compare/BDSG +comparison/MS +compartment/SM +compartmental +compartmentalization/M +compartmentalize/DSG +compass/GMDS +compassion/M +compassionate/Y +compatibility/IM +compatible/IMS +compatibly/I +compatriot/MS +compeer/SM +compel/S +compelled +compelling/Y +compendious +compendium/SM +compensate/DSXGN +compensated/U +compensation/M +compensatory +compere/DSG +compete/DSG +competence/IM +competences +competencies +competency/IM +competent/IY +competition/SM +competitive/PY +competitiveness/M +competitor/SM +compilation/SM +compile/DRSZG +compiler/M +complacence/M +complacency/M +complacent/Y +complain/DRZGS +complainant/MS +complainer/M +complaint/SM +complaisance/M +complaisant/Y +complected +complement/SGMD +complementary +complete/PYTGNXDRS +completed/U +completeness/IM +completion/M +complex/MSY +complexion/MDS +complexional +complexity/SM +compliance/M +compliant/Y +complicate/GDS +complicated/Y +complication/M +complicit +complicity/M +compliment/MDGS +complimentary/U +comply/NDSXG +compo/S +component/SM +comport/LSGD +comportment/M +compose/AECGSD +composedly +composer/MS +composite/MYGNXDS +composition/CM +compositional +compositor/SM +compost/SGMD +composure/EM +compote/SM +compound/GMDBS +compounded/U +comprehend/SDG +comprehensibility/IM +comprehensible/I +comprehensibly/I +comprehension/IM +comprehensions +comprehensive/PMYS +comprehensiveness/M +compress's +compress/CGVDS +compressed/U +compressible +compression/CM +compressor/SM +comprise/GDS +compromise/MGDS +comptroller/MS +compulsion/MS +compulsive/YP +compulsiveness/M +compulsorily +compulsory/SM +compunction/SM +computation/SM +computational/Y +compute/ADSG +computer/MS +computerate +computerization/M +computerize/GDS +computing/M +comrade/SMY +comradeship/M +con/GSM +concatenate/XDSGN +concatenation/M +concave/YP +concaveness/M +conceal/SDRZGBL +concealed/U +concealer/M +concealment/M +conceit/SMD +conceited/PY +conceitedness/M +conceivable/I +conceivably/I +conceive/DSGB +concentrate/DSMGNX +concentration/M +concentric +concentrically +concept/SM +conception/SM +conceptional +conceptual/Y +conceptualization/MS +conceptualize/DSG +concern/UMD +concerned/UY +concerning +concerns +concert's +concert/ESDG +concerted/Y +concertgoer/S +concertina/SGMD +concertize/DSG +concertmaster/MS +concerto/SM +concessionaire/MS +concessional +concessionary +conch/M +conchie/S +conchs +concierge/MS +conciliate/DSGN +conciliation/AM +conciliator/SM +conciliatory +concise/RPYTN +conciseness/M +concision/M +conclave/SM +conclude/DSG +conclusion/MS +conclusive/IYP +conclusiveness/IM +concoct/SDG +concoction/MS +concomitant/MYS +concord/M +concordance/SM +concordant +concordat/SM +concourse/SM +concrete/DSPMYGNX +concreteness/M +concretion/M +concubinage/M +concubine/MS +concupiscence/M +concupiscent +concur/S +concurred +concurrence/SM +concurrency +concurring +concuss/V +concussion/SM +condemn/SDRZG +condemnation/MS +condemnatory +condemner/M +condensate/MNXS +condensation/M +condense/DRSZG +condenser/M +condescending/Y +condescension/M +condign +condiment/MS +condition's +condition/AGSD +conditional/SMY +conditionality +conditioned/U +conditioner/SM +conditioning/M +condo/SM +condolence/SM +condom/SM +condominium/MS +condone/DSG +condor/SM +conduce/DSGV +conduct/MDGV +conductance/M +conductibility/M +conductible +conduction/M +conductivity/M +conductor/MS +conductress/MS +conduit/SM +cone/M +coneys +confab/SM +confabbed +confabbing +confabulate/XDSGN +confabulation/M +confection/SZMR +confectioner/M +confectionery/SM +confederacy/SM +confederate/M +confer/S +conferee/SM +conference/MGS +conferrable +conferral/M +conferred +conferrer/MS +conferring +confessed/Y +confession/SM +confessional/SM +confessor/MS +confetti/M +confidant/MS +confidante/SM +confide/DRSZG +confidence/SM +confident/Y +confidential/Y +confidentiality/M +confider/M +confiding/Y +configuration/S +configure/B +confined/U +confinement/MS +confirm/ASDG +confirmation/ASM +confirmatory +confirmed/U +confiscate/DSGNX +confiscation/M +confiscator/SM +confiscatory +conflagration/MS +conflate/XDSGN +conflation/M +conflict/SGMD +confluence/MS +confluent +conform/ZB +conformable/U +conformal +conformance/M +conformism/M +conformist/SM +conformity/M +confrere/MS +confrontation/SM +confrontational +confuse/RZ +confused/Y +confusing/Y +confutation/M +confute/DSG +conga/SMDG +congeal/SLDG +congealment/M +conger/SM +congeries/M +congest/SDGV +congestion/M +conglomerate/DSXMGN +conglomeration/M +congrats/M +congratulate/XGNDS +congratulation/M +congratulatory +congregant/MS +congregate/GNDSX +congregation/M +congregational +congregationalism/M +congregationalist/MS +congress/MS +congressional +congressman/M +congressmen +congresspeople +congressperson/MS +congresswoman/M +congresswomen +congruence/M +congruent/Y +congruity/ISM +congruous +conic/SM +conical/Y +conifer/SM +coniferous +conjectural +conjecture/MGDS +conjoint +conjugal/Y +conjugate/DSXGN +conjugation/M +conjunct/VMS +conjunctiva/SM +conjunctive/SM +conjunctivitis/M +conjuration/MS +conjure/DRSZG +conjurer/M +conk/MDRZ +conman +connect/AEDVGS +connectable +connected/U +connection/EMS +connective/MS +connectivity/M +connector/MS +conned +conning +conniption/MS +connivance/M +connive/DRSZG +conniver/M +connoisseur/SM +connotative +connubial +conquer/ASDG +conquerable/U +conquered/U +conqueror/MS +conquest/AM +conquistador/SM +cons/DSG +consanguineous +consanguinity/M +conscienceless +conscientious/PY +conscientiousness/M +conscious/UYP +consciousness/UM +consciousnesses +conscription/M +consecrate/ADSGN +consecrated/U +consecration/AM +consecrations +consecutive/Y +consensual +consensus/MS +consent/SMDG +consequence/SM +consequent/Y +consequential/IY +conservancy/SM +conservation/M +conservationism/M +conservationist/SM +conservatism/M +conservative/MYS +conservatoire/S +conservator/SM +conservatory/SM +consider/AGSD +considerable/I +considerably +considerate/IPYN +considerateness/IM +consideration/AIM +considerations +considered/U +consign/ASDG +consignee/MS +consignment/MS +consist/SDG +consistence/MS +consistency/ISM +consistent/IY +consistory/SM +consolable/I +consolation/MS +consolatory +consolidate/XDSGN +consolidated/U +consolidation/M +consolidator/MS +consoling/Y +consomme/M +consonance/SM +consonant/SMY +consortia +consortium/M +conspectus/MS +conspicuous/IPY +conspicuousness/IM +conspiracy/SM +conspirator/MS +conspiratorial/Y +conspire/GD +constable/SM +constabulary/SM +constancy/IM +constant/MYS +constellation/SM +consternation/M +constipate/GNDS +constipation/M +constituency/SM +constituent/SM +constitute/ADSGNV +constitution/AM +constitutional/MYS +constitutionalism +constitutionality/UM +constitutions +constrained/U +constraint/SM +constrict/GVSD +constriction/SM +constrictor/SM +construable +construct's +construct/CADVGS +construction/CAMS +constructional +constructionist's +constructionist/CS +constructive/YP +constructiveness/M +constructor/MS +construe/GDS +consul/KSM +consular/K +consulate/SM +consulship/M +consult/GSD +consultancy/SM +consultant/MS +consultation/MS +consultative +consumable/SM +consume/BDRSZG +consumed/U +consumer/M +consumerism/M +consumerist/MS +consummate/YGNXDS +consummated/U +consumption/M +consumptive/SM +cont +contact/ASDG +contactable +contactless +contagion/MS +contagious/PY +contagiousness/M +contain/SBLDRZG +container/M +containerization/M +containerize/DSG +containment/M +contaminant/SM +contaminate/ACDSG +contaminated/U +contamination/CM +contaminator/SM +contd +contemn/SDG +contemplate/DSGNV +contemplation/M +contemplative/SMY +contemporaneity/M +contemporaneous/Y +contempt/M +contemptible +contemptibly +contemptuous/YP +contemptuousness/M +contender/MS +content/ESLMDG +contented/EY +contentedness/M +contention/SM +contentious/YP +contentiousness/M +contently +contentment/EM +conterminous/Y +contestable/I +contestant/MS +contested/U +contextualization +contextualize/DSG +contiguity/M +contiguous/Y +continence/IM +continent/SM +continental/SM +contingency/SM +contingent/SMY +continua +continual/Y +continuance/EMS +continuation/EMS +continue/EGDS +continuity/ESM +continuous/EY +continuum/M +contort/GD +contortion/MS +contortionist/SM +contra +contraband/M +contrabassoon/S +contraception/M +contraceptive/SM +contract/MDG +contractible +contractile +contractility +contraction/S +contractual/Y +contradict/SDG +contradiction/SM +contradictory +contradistinction/MS +contraflow/S +contrail/MS +contraindicate/GNXDS +contraindication/M +contralto/SM +contraption/SM +contrapuntal/Y +contrarian/SM +contrarianism +contrariety/M +contrarily +contrariness/M +contrariwise +contrary/PSM +contrast/MDGS +contravene/GDS +contravention/SM +contretemps/M +contribute/XGND +contribution/M +contributor/MS +contributory +contrition/M +contrivance/MS +contrive/ZGDRS +contriver/M +control's +control/CS +controllable/U +controlled/UC +controller/MS +controlling/C +controversial/Y +controversy/SM +controvert/DSG +controvertible/I +contumacious/Y +contumacy/M +contumelious +contumely/SM +contuse/XDSGN +contusion/M +conundrum/SM +conurbation/MS +convalesce/DSG +convalescence/MS +convalescent/SM +convection/M +convectional +convective +convector/S +convene/ADSG +convener/MS +convenience/IMS +convenient/IY +convent/SM +conventicle/MS +convention/SM +conventional/UY +conventionality/UM +conventionalize/GDS +conventioneer/S +convergence/MS +convergent +conversant +conversation/MS +conversational/Y +conversationalist/SM +converse/Y +convert's +convert/AGSD +converted/U +converter/SM +convertibility/M +convertible/SM +convex/Y +convexity/M +convey/SBDG +conveyance/MGS +conveyor/MS +convict/GSMD +conviction/MS +convince/GDS +convinced/U +convincing/UY +convivial/Y +conviviality/M +convoke/DSG +convoluted +convolution/MS +convoy/SMDG +convulse/GNVXDS +convulsion/M +convulsive/Y +cony/M +coo/GSMD +cook's +cook/ADGS +cookbook/MS +cooked/U +cooker/SM +cookery/SM +cookhouse/S +cookie/SM +cooking/M +cookout/SM +cookware/SM +cool/MDRYZTGPS +coolant/SM +cooler/M +coolie/SM +coolness/M +coon/MS! +coonskin/MS +coop/MDRZGS +cooper/MDG +cooperage/M +cooperate/DSGNV +cooperation/M +cooperative/PMYS +cooperativeness/M +cooperator/SM +coordinate/DSMYGN +coordinated/U +coordination/M +coordinator/MS +coot/MS +cootie/SM +cop/GJSMD +copacetic +copay/M +cope/MS +copier/SM +copilot/SM +coping/M +copious/PY +copiousness/M +copped +copper/SM +copperhead/SM +copperplate/M +coppery +copping +copra/M +copse/SM +copter/SM +copula/SM +copulate/GNVDS +copulation/M +copulative/SM +copy's +copy/ADSG +copybook/SM +copycat/MS +copycatted +copycatting +copyist/MS +copyleft +copyright/GSMD +copywriter/MS +coquetry/SM +coquette/DSMG +coquettish/Y +cor +coracle/SM +coral/SM +corbel/SM +cord/EASGDM +cordage/M +cordial/SMY +cordiality/M +cordillera/MS +cordite/M +cordless +cordon/SMDG +cordovan/M +corduroy/MS +corduroys/M +core/MZGDRS +coreligionist/S +corer/M +corespondent/MS +corgi/SM +coriander/M +cork's +cork/UDGS +corkage +corker/SM +corkscrew/SMDG +corm/MS +cormorant/SM +corn/MDRZGS +cornball/MS +cornbread/M +corncob/MS +corncrake/S +cornea/SM +corneal +corner/GMD +cornerstone/SM +cornet/SM +cornfield/S +cornflakes/M +cornflour +cornflower/SM +cornice/MS +cornily +corniness/M +cornmeal/M +cornrow/MDGS +cornstalk/SM +cornstarch/M +cornucopia/MS +corny/PRT +corolla/MS +corollary/SM +corona/SM +coronal/MS +coronary/SM +coronation/SM +coronavirus/MS +coroner/MS +coronet/MS +corp +corpora +corporal/SM +corporate/XYN +corporation/IM +corporatism +corporeal/Y +corporeality/M +corps/MS +corpse/M +corpsman/M +corpsmen +corpulence/M +corpulent +corpus/M +corpuscle/MS +corpuscular +corr +corral/SM +corralled +corralling +correct/DRYTGVSBP +corrected/U +correction/SM +correctional +corrective/SM +correctness/IM +corrector +correlate/XDSMGNV +correlated/U +correlation/M +correlational +correlative/MS +correspond/SDG +correspondence/SM +correspondent/SM +corresponding/Y +corridor/SM +corrie/S +corroborate/GNVDSX +corroborated/U +corroboration/M +corroborator/SM +corroboratory +corrode/GDS +corrosion/M +corrosive/SMY +corrugate/GNXDS +corrugation/M +corrupt/DRYPSTG +corruptibility/IM +corruptible/I +corruption/MS +corruptness/M +corsage/MS +corsair/MS +corset/SGMD +cortege/MS +cortex/M +cortical +cortices +cortisol +cortisone/M +corundum/M +coruscate/GNDS +coruscation/M +corvette/SM +cos/M +cosh/DSG +cosign/ZGSDR +cosignatory/SM +cosigner/M +cosine/SM +cosmetic/SM +cosmetically +cosmetician/MS +cosmetologist/MS +cosmetology/M +cosmic +cosmically +cosmogonist/SM +cosmogony/SM +cosmological +cosmologist/SM +cosmology/SM +cosmonaut/SM +cosmopolitan/MS +cosmopolitanism/M +cosmos/MS +cosplay +cosponsor/GSMD +cosset/SGD +cossetted +cossetting +cost/MDYGSJ +costar/SM +costarred +costarring +costliness/M +costly/PTR +costume/MZGDRS +costumer/M +costumier/S +cot/SM +cotangent/MS +cote/MS +coterie/MS +coterminous +cotillion/SM +cottage/MZGRS +cottager/M +cottar/SM +cotter/SM +cotton/SGMD +cottonmouth/M +cottonmouths +cottonseed/MS +cottontail/MS +cottonwood/SM +cottony +cotyledon/MS +couch/MDSG +couchette/S +cougar/SM +cough/MDG +coughs +could +could've +couldn't +coulee/SM +coulis +coulomb/MS +council/MS +councilman/M +councilmen +councilor/MS +councilperson/SM +councilwoman/M +councilwomen +counsel/JMDGS +counselor/MS +count/EASMDG +countable/U +countably +countdown/MS +counted/U +countenance's +countenance/EGDS +counter/EMS +counteract/SGVD +counteraction/MS +counterargument/S +counterattack/GMDS +counterbalance/MGDS +counterblast/S +counterclaim/GSMD +counterclockwise +counterculture/SM +countered +counterespionage/M +counterexample/S +counterfactual +counterfeit/ZGMDRS +counterfeiter/M +counterfoil/MS +countering +counterinsurgency/SM +counterintelligence/M +counterman/M +countermand/GMDS +countermeasure/SM +countermelody/S +countermen +countermove/S +counteroffensive/SM +counteroffer/SM +counterpane/SM +counterpart/SM +counterpetition +counterpoint/MDGS +counterpoise/MGDS +counterproductive +counterrevolution/SM +counterrevolutionary/SM +countersign/GSMD +countersignature/MS +countersink/GSM +counterspy/SM +counterstroke/SM +countersunk +countertenor/MS +countervail/GSD +counterweight/MS +countess/MS +countless +countrified +country/SM +countryman/M +countrymen +countryside/MS +countrywide +countrywoman/M +countrywomen +county/SM +countywide +coup's +coup/AS +coupe/SM +couple's +couple/UCGSD +couplet/MS +coupling/SM +coupon/SM +courage/M +courageous/YP +courageousness/M +courgette/S +courier/MDSG +course/EDGMS +coursebook/S +courser/MS +coursework +court/SMDYG +courteous/EY +courteousness/M +courtesan/SM +courtesy/ESM +courthouse/MS +courtier/SM +courtliness/M +courtly/PRT +courtroom/MS +courtship/MS +courtyard/MS +couscous/M +cousin/SM +couture/M +couturier/MS +covalent +covariance +covariant +cove/MS +coven/SM +covenant/MDSG +cover's +cover/AEUGDS +coverage/M +coverall/MS +covering's +coverings +coverlet/MS +covert/SPMY +covertness/M +covet/SDG +covetous/YP +covetousness/M +covey/SM +cow/ZGSMDR +coward/SMY +cowardice/M +cowardliness/M +cowbell/MS +cowbird/MS +cowboy/SM +cowcatcher/MS +cower/DG +cowgirl/MS +cowhand/MS +cowherd/MS +cowhide/MS +cowl/MGSJ +cowlick/MS +cowling/M +cowman/M +cowmen +coworker/MS +cowpat/S +cowpoke/MS +cowpox/M +cowpuncher/SM +cowrie/SM +cowshed/S +cowslip/SM +cox/GDS +coxcomb/MS +coxswain/MS +coy/TPRY +coyness/M +coyote/SM +coypu/SM +cozen/SDG +cozenage/M +cozily +coziness/M +cozy/RSMTP +cpd +cpl +cps +crab/MS +crabbed +crabber/SM +crabbily +crabbiness/M +crabbing +crabby/PRT +crabgrass/M +crablike +crabwise +crack/SMDRYZGJ +crackdown/MS +cracker/M +crackerjack/MS +crackhead/MS +crackle/DSJMG +crackling/M +crackpot/MS +crackup/SM +cradle/DSMG +craft/SMDG +craftily +craftiness/M +craftsman/M +craftsmanship/M +craftsmen +craftspeople +craftswoman/M +craftswomen +crafty/RTP +crag/MS +cragginess/M +craggy/RPT +cram/S +crammed +crammer/S +cramming +cramp/SMDG +cramping/M +crampon/SM +cranberry/SM +crane/DSMG +cranial +cranium/SM +crank/SMDG +crankcase/SM +crankily +crankiness/M +crankshaft/MS +cranky/PRT +cranny/DSM +crap/MS +crape/SM +crapped +crapper/S +crappie/RSMT +crapping +crappy +craps/M +crapshooter/MS +crash/MDSG +crass/RYTP +crassness/M +crate/DRSMZG +crater/MDG +cravat/SM +crave/DSGJ +craven/SMYP +cravenness/M +craving/M +craw/MS +crawdad/SM +crawl/SMDRZG +crawler/M +crawlspace/SM +crawly/TRSM +cray/S +crayfish/MS +crayola/S +crayon/GSMD +craze/DSMG +crazily +craziness/M +crazy/PRSMT +creak/SMDG +creakily +creakiness/M +creaky/RPT +cream/SMDRZG +creamer/M +creamery/SM +creamily +creaminess/M +creamy/RPT +crease/ICGMSD +create/KADSGNV +creation's/K +creation/ASM +creationism/SM +creationist/SM +creative/SMYP +creativeness/M +creativity/M +creator/MS +creature/SM +creche/SM +cred +credence/M +credential/SGMD +credenza/SM +credibility/IM +credible/I +credibly/I +credit/EGSBMD +creditably/E +creditor/SM +creditworthy/P +credo/SM +credulity/IM +credulous/IY +credulousness/M +creed/SM +creek/SM +creel/SM +creep/SMRZG +creeper/M +creepily +creepiness/M +creepy/TPR +cremains/M +cremate/GNDSX +cremation/M +crematoria +crematorium/MS +crematory/SM +creme/SM +crenelate/XGNDS +crenelation/M +creole/SM +creosote/MGDS +crepe/SM +crept +crepuscular +crescendo/CSM +crescent/MS +cress/M +crest/SMDG +crestfallen +crestless +cretaceous +cretin/SM +cretinism/M +cretinous +cretonne/M +crevasse/SM +crevice/MS +crew/MDGS +crewel/M +crewelwork/M +crewman/M +crewmen +crib/MS +cribbage/M +cribbed +cribber/MS +cribbing +crick/SMDG +cricket/MRSZG +cricketer/M +crier/M +crikey +crime/SM +criminal/MYS +criminality/M +criminalize/CGDS +criminologist/MS +criminology/M +crimp/SMDG +crimson/SMDG +cringe/DSMG +crinkle/DSMG +crinkly/RT +crinoline/SM +cripes +cripple/DRSMZG +crippler/M +crippleware +crippling/Y +crises +crisis/M +crisp/SMDRYTGP +crispbread/S +crispiness/M +crispness/M +crispy/PRT +crisscross/GMDS +criteria +criterion/M +critic/SM +critical/UY +criticality +criticism/MS +criticize/ZGDRS +criticizer/M +critique/MGDS +critter/SM +croak/SMDG +croaky/RT +crochet/SMDRZG +crocheter/M +crocheting/M +crock/SMD +crockery/M +crocodile/SM +crocus/MS +croft/SRZG +croissant/MS +crone/SM +crony/SM +cronyism/M +crook/SMDG +crooked/PTRY +crookedness/M +crookneck/SM +croon/SMDRZG +crooner/M +crop/MS +cropland/SM +cropped +cropper/MS +cropping +croquet/M +croquette/SM +crosier/MS +cross's +cross/AUGTSD +crossbar/SM +crossbeam/MS +crossbones/M +crossbow/SM +crossbowman/M +crossbowmen +crossbred +crossbreed/SGM +crosscheck/SMDG +crosscurrent/MS +crosscut/SM +crosscutting +crosser +crossfire/MS +crosshatch/GDS +crossing/SM +crossly +crossness/M +crossover/MS +crosspatch/MS +crosspiece/SM +crossroad/MS +crossroads/M +crosstown +crosswalk/MS +crosswind/MS +crosswise +crossword/MS +crotch/MS +crotchet/SM +crotchety +crouch/GMDS +croup/M +croupier/M +croupy/ZTR +crouton/MS +crow/MDGS +crowbar/MS +crowd/SMDG +crowded/U +crowdfund/SDG +crowfeet +crowfoot/SM +crown/SMDG +crowned/U +crucial/Y +crucible/SM +crucifix/MS +crucifixion/SM +cruciform/SM +crucify/DSG +crud/M +cruddy/TR +crude/RMYTP +crudeness/M +crudites/M +crudity/SM +cruel/RYPT +cruelness/M +cruelty/SM +cruet/SM +cruft/SD +crufty +cruise/DRSMZG +cruiser/M +cruller/MS +crumb/SMDYG +crumble/MGDS +crumbliness/M +crumbly/TPR +crumby/TR +crumminess/M +crummy/PTR +crumpet/MS +crumple/MGDS +crunch/GMDRS +crunchiness/M +crunchy/TRP +crupper/MS +crusade/MZGDRS +crusader/M +cruse/SM +crush/MDRSZG +crusher/M +crushing/Y +crust/SMDG +crustacean/SM +crustal +crustily +crustiness/M +crusty/TRP +crutch/MS +crux/MS +cry/ZGJDRSM +crybaby/SM +cryogenic/S +cryogenics/M +cryonics +cryosurgery/M +crypt/SM +cryptanalysis +cryptic +cryptically +cryptocurrency/SM +cryptogram/SM +cryptographer/SM +cryptography/M +crystal/SM +crystalline +crystallization/M +crystallize/ADSG +crystallographic +crystallography +ct +ctn +ctr +cu +cub/ZGSMDR +cubbyhole/MS +cube/MS +cuber/M +cubic +cubical +cubicle/MS +cubism/M +cubist/SM +cubit/SM +cuboid/S +cuckold/MDSG +cuckoldry/M +cuckoo/SM +cucumber/SM +cud/SM +cuddle/DSMG +cuddly/TR +cudgel/SGMDJ +cue/DSMG +cuff/MDGS +cuisine/SM +culinary +cull/MDGS +culminate/XDSGN +culmination/M +culotte/SM +culpability/M +culpable/I +culpably +culprit/SM +cult/MS +cultism/M +cultist/MS +cultivable +cultivar/SM +cultivate/BDSGN +cultivated/U +cultivation/M +cultivator/MS +cultural/Y +culture/MGDS +cultured/U +culvert/MS +cum/SM +cumber/SDG +cumbersome/P +cumbersomeness/M +cumbrous +cumin/M +cummerbund/MS +cumming +cumulative/Y +cumuli +cumulonimbi +cumulonimbus/M +cumulus/M +cuneiform/M +cunnilingus/M +cunning/MRYT +cunt/MS! +cup/SM +cupboard/SM +cupcake/MS +cupful/SM +cupid/SM +cupidity/M +cupola/SMD +cuppa/S +cupped +cupping +cupric +cur/SMY +curability/M +curacao +curacy/SM +curare/M +curate/DSMGV +curative/MS +curator/KMS +curatorial +curb/MDGS +curbing/M +curbside +curbstone/SM +curd/MS +curdle/DSG +cure's +cure/KZGBDRS +cured/U +curer/KM +curettage/M +curfew/SM +curia/M +curiae +curie/SM +curio/SM +curiosity/SM +curious/YP +curiousness/M +curium/M +curl's +curl/UDGS +curler/SM +curlew/SM +curlicue/DSMG +curliness/M +curling/M +curly/RPT +curmudgeon/MYS +currant/MS +currency/SM +current's +current/FAY +currents +curricula +curricular +curriculum/M +curry/DSMG +currycomb/SGMD +curse/DSMGV +cursed/Y +cursive's +cursive/EAY +cursor/SM +cursorily +cursoriness/M +cursory/P +curt/RYTP +curtail/GDSL +curtailment/SM +curtain/GMDS +curtness/M +curtsy/GDSM +curvaceous/P +curvaceousness/M +curvature/SM +curve/DSMG +curvy/RT +cushion/MDSG +cushy/RT +cusp/MS +cuspid/SM +cuspidor/SM +cuss's +cuss/FEGSD +cussed/PY +custard/MS +custodial +custodian/MS +custodianship/M +custody/M +custom/SZMR +customarily +customary/U +customer/M +customhouse/SM +customization/M +customize/DSG +cut/TSMR +cutaneous +cutaway/MS +cutback/MS +cute/YP +cuteness/M +cutesy/TR +cutey/S +cuticle/MS +cutie/SM +cutlass/MS +cutler/SM +cutlery/M +cutlet/SM +cutoff/SM +cutout/SM +cutter/SM +cutthroat/SM +cutting/MYS +cuttlefish/MS +cutup/SM +cutworm/MS +cw +cwt +cyan/M +cyanide/M +cyanobacteria +cyberbully/SM +cybercafe/S +cybernetic/S +cybernetics/M +cyberpunk/SM +cybersex +cyberspace/MS +cyborg/SM +cyclamen/MS +cycle/ADSMG +cyclic +cyclical/Y +cyclist/MS +cyclometer/MS +cyclone/MS +cyclonic +cyclopedia/MS +cyclopes +cyclops/M +cyclotron/MS +cygnet/MS +cylinder/MS +cylindrical +cymbal/MS +cymbalist/MS +cynic/SM +cynical/Y +cynicism/M +cynosure/MS +cypress/MS +cyst/MS +cystic +cystitis +cytokines +cytologist/SM +cytology/M +cytoplasm/M +cytoplasmic +cytosine/M +czar/MS +czarina/SM +czarism +czarist/SM +d'Arezzo/M +d'Estaing/M +d/NXGJ +dB +dab/SM +dabbed +dabber/MS +dabbing +dabble/ZGDRS +dabbler/M +dace/SM +dacha/MS +dachshund/MS +dactyl/MS +dactylic/MS +dad/SM +dadaism/M +dadaist/MS +daddy/SM +dado/M +dadoes +daemon/MS +daemonic +daffiness/M +daffodil/SM +daffy/PTR +daft/PTRY +daftness/M +dag/S +dagger/MS +dago/S +dagoes +daguerreotype/DSMG +dahlia/MS +dailiness/M +daily/PSM +daintily +daintiness/M +dainty/RSMTP +daiquiri/MS +dairy/GSM +dairying/M +dairymaid/MS +dairyman/M +dairymen +dairywoman/M +dairywomen +dais/MS +daisy/SM +dale/SM +dalliance/MS +dallier/M +dally/ZGDRS +dalmatian/MS +dam/SM +damage/MGDS +damageable +damaged/U +damages/M +damask/MDGS +dame/SM +dammed +damming +dammit +damn/SBGMD +damnably +damnation/M +damned/T +damp/SPXZTGMDNRY +dampen/ZGDR +dampener/M +damper/M +dampness/M +damsel/MS +damselfly/SM +damson/MS +dance/MZGDRS +dancer/M +dancing/M +dandelion/SM +dander/M +dandify/GDS +dandle/GDS +dandruff/M +dandy/TRSM +dang/SZGDR +danger/M +dangerous/Y +dangle/ZGDRS +dangler/M +danish/MS +dank/PTRY +dankness/M +danseuse/MS +dapper/TR +dapple/MGDS +dare/DRSMZG +daredevil/MS +daredevilry/M +darer/M +daresay +daring/MY +dark/PXTMNRY +darken/ZGDR +darkener/M +darkie/S +darkness/M +darkroom/MS +darling/MS +darn/SZGMDR +darned/TR +darner/M +dart/SZGMDR +dartboard/MS +darter/M +dash/ZGMDRS +dashboard/SM +dasher/M +dashiki/MS +dashing/Y +dastard/MYS +dastardliness/M +data +database/SM +dataset/MS +datatype +date/DRSMZGV +datebook/S +dated/U +dateless +dateline/MGDS +dater/M +dateset +dative/MS +datum/M +daub/SZGMDR +dauber/M +daughter/SMY +daunt/GDS +daunting/Y +dauntless/YP +dauntlessness/M +dauphin/MS +davenport/MS +davit/MS +dawdle/ZGDRS +dawdler/M +dawn/SGMD +day/SM +daybed/MS +daybreak/M +daycare/M +daydream/MDRZGS +daydreamer/M +daylight/MS +daylights/M +daylong +daytime/M +daze/DSMG +dazed/Y +dazzle/MZGDRS +dazzler/M +dazzling/Y +db +dbl +dc +dd/SDG +dded/K +dding/K +deacon/MS +deaconess/MS +dead/XTMNRY +deadbeat/MS +deadbolt/SM +deaden/GD +deadhead/SDG +deadline/SM +deadliness/M +deadlock/GSMD +deadly/TPR +deadpan/MS +deadpanned +deadpanning +deadwood/M +deaf/PXTNR +deafen/GD +deafening/Y +deafness/M +deal/SJZGMR +dealer/M +dealership/SM +dealing/M +dealt +dean/M +deanery/SM +deanship/M +dear/SPTMRYH +dearest/S +dearness/M +dearth/M +dearths +deary/SM +death/MY +deathbed/SM +deathblow/MS +deathless/Y +deathlike +deaths +deathtrap/MS +deathwatch/MS +deaves +deb/SM +debacle/MS +debarkation/M +debarment/M +debate/BMZR +debater/M +debating/M +debauch/MDSG +debauchee/MS +debauchery/SM +debenture/MS +debilitate/DSGN +debilitation/M +debility/SM +debit/D +debonair/PY +debonairness/M +debouch/GDS +debridement +debris/M +debt/SM +debtor/MS +debugger/S +debut/GMD +debutante/SM +decade/MS +decadence/M +decadency/M +decadent/MYS +decaf/MS +decaffeinate/DSG +decagon/MS +decal/MS +decampment/M +decapitate/XGNDS +decapitator/MS +decathlete/S +decathlon/SM +decay/GD +deceased/M +decedent/MS +deceit/MS +deceitful/YP +deceitfulness/M +deceive/UGDS +deceiver/MS +deceiving/Y +decelerate/GNDS +deceleration/M +decelerator/SM +decency/ISM +decennial/SM +decent/IY +deception/MS +deceptive/YP +deceptiveness/M +decibel/MS +decidable/U +decide/BZGDRS +decided/Y +deciduous +deciliter/MS +decimal/SM +decimalization +decimate/DSGN +decimation/M +decimeter/MS +decipherable/UI +decision/IM +decisions +decisive/IPY +decisiveness/IM +deck/SGMD +deckchair/S +deckhand/SM +deckle/S +declamation/MS +declamatory +declaration/MS +declarative +declaratory +declare/DRSZGB +declared/U +declarer/M +declension/SM +declination/M +decline/DRSMZG +decliner/M +declivity/SM +decoherence +decolletage/SM +decollete +decongestant/MS +deconstructionism +decor/MS +decorate/AGNVDS +decorating/M +decoration/AM +decorations +decorative/Y +decorator/MS +decorous/IY +decorousness/M +decorum/M +decoupage/DSMG +decoy/GMDS +decreasing/Y +decree/MDS +decreeing +decrement/GDS +decrepit +decrepitude/M +decriminalization/M +decry/GDS +decryption +dedicate/AGDS +dedication/SM +dedicator/SM +dedicatory +deduce/GDS +deducible +deduct/GVD +deductible/SM +deduction/SM +deductive/Y +deed/GD +deejay/MS +deem/ASGD +deep/SPXTMNRY +deepen/GD +deepfake/SM +deepness/M +deer/M +deerskin/M +deerstalker/S +def/Z +defacement/M +defacer/SM +defalcate/DSXGN +defalcation/M +defamation/M +defamatory +defame/ZGDRS +defamer/M +defaulter/SM +defeat/MDRZGS +defeated/U +defeater/M +defeatism/M +defeatist/MS +defecate/GNDS +defecation/M +defect/MDGVS +defection/MS +defective/MPYS +defectiveness/M +defector/MS +defendant/SM +defended/U +defenestration/S +defense/DSMGV +defenseless/YP +defenselessness/M +defensible/I +defensibly/I +defensive/MYP +defensiveness/M +deference/M +deferential/Y +deferral/MS +deferred +deferring +deffer +deffest +defiant/Y +defibrillation +defibrillator/S +deficiency/SM +deficient +deficit/SM +defilement/M +definable/IU +define/AGDS +defined/U +definer/MS +definite/IYVP +definiteness/IM +definition/AM +definitions +definitive/Y +deflate/GNDS +deflation/M +deflationary +deflect/DGVS +deflection/MS +deflector/SM +defogger/SM +defoliant/SM +defoliate/DSGN +defoliation/M +defoliator/MS +deformity/SM +defraud/DRZGS +defrauder/M +defrayal/M +defrock/DG +defroster/MS +deft/PTRY +deftness/M +defunct +defy/GDS +deg +degeneracy/M +degenerate/MV +degrade/B +degree/MS +dehydrator/SM +dehydrogenase +deicer/MS +deification/M +deify/NGDS +deign/GDS +deist/MS +deistic +deity/SM +deject/GDS +dejected/Y +dejection/M +delay/ZDR +delectable +delectably +delectation/M +delegate/GD +delete/XGNDS +deleterious +deletion/M +delft/M +delftware/M +deli/SM +deliberate/XYVP +deliberateness/M +delicacy/ISM +delicate/IY +delicateness/M +delicatessen/SM +delicious/PY +deliciousness/M +delighted/Y +delightful/Y +deliminator +delineate/GNXDS +delineation/M +delinquency/SM +delinquent/SMY +deliquesce/DSG +deliquescent +delirious/YP +deliriousness/M +delirium/SM +deliver/ADGS +deliverable/S +deliverance/M +delivered/U +deliverer/SM +dell/SM +delphinium/MS +delta/MS +delude/GDS +deluge/MGDS +delusion/MS +delusional +delusive/Y +deluxe +delve/ZGDRS +delver/M +demagogic +demagogically +demagogue/SM +demagoguery/M +demagogy/M +demand/GMDS +demanding/U +demarcate/DSGNX +demarcation/M +demean/GDS +demeanor/M +demented/Y +dementia/M +demesne/MS +demigod/MS +demigoddess/MS +demijohn/SM +demimondaine/SM +demimonde/M +demise/MGDS +demitasse/MS +demo/GMD +democracy/SM +democrat/MS +democratic/U +democratically +democratization/M +democratize/GDS +demode +demographer/SM +demographic/SM +demographically +demographics/M +demography/M +demolish/DSG +demolition/MS +demon/MS +demonetization/M +demoniac +demoniacal/Y +demonic +demonically +demonize/GDS +demonology/SM +demonstrability +demonstrable/I +demonstrably +demonstrate/XGNVDS +demonstration/M +demonstrative/MYSP +demonstrativeness/M +demonstrator/MS +demote/GD +demotic +demount +demulcent/SM +demur/TMRS +demure/PY +demureness/M +demurral/SM +demurred +demurrer/SM +demurring +den/M +denationalization +denaturation +denature/DG +dendrite/SM +dengue/M +deniability +deniable/U +denial/MS +denier/M +denigrate/DSGN +denigration/M +denim/MS +denitrification +denizen/MS +denominational +denotative +denouement/MS +denounce/LDSG +denouncement/SM +dense/PYTR +denseness/M +density/SM +dent/ISGMD +dental/Y +dentifrice/SM +dentin/M +dentist/MS +dentistry/M +dentition/M +denture/IMS +denuclearize/GDS +denudation/M +denude/GDS +denunciation/SM +deny/ZGDRS +deodorant/SM +deodorization/M +deodorize/DRSZG +deodorizer/M +departed/M +department/MS +departmental/Y +departmentalization/M +departmentalize/GDS +departure/SM +dependability/M +dependable/U +dependably +dependence/IM +dependency/SM +dependent/IMYS +depict/GDS +depiction/MS +depilatory/SM +deplete/GNDS +depletion/M +deplorably +deplore/BGDS +deploy/ALGDS +deployment/AM +deployments +deponent/MS +deportation/MS +deportee/MS +deportment/M +deposit/AGMDS +depositor/MS +depository/SM +deprave/GDS +depravity/SM +deprecate/GNDS +deprecating/Y +deprecation/M +deprecatory +depreciate/DSGN +depreciation/M +depredation/SM +depressant/SM +depressing/Y +depression/SM +depressive/SM +depressor/MS +depressurization +deprive/GDS +deprogramming +depth/M +depths +deputation/MS +depute/DSG +deputize/DSG +deputy/SM +derailleur/SM +derailment/SM +derangement/M +derby/SM +derelict/MS +dereliction/M +deride/GDS +derision/M +derisive/PY +derisiveness/M +derisory +derivation/MS +derivative/MS +derive/B +dermal +dermatitis/M +dermatological +dermatologist/SM +dermatology/M +dermis/M +derogate/DSGN +derogation/M +derogatorily +derogatory +derrick/SM +derriere/SM +derringer/SM +derv +dervish/MS +desalinate/GNDS +desalination/M +desalinization/M +desalinize/GDS +descant/M +descend/FGDS +descendant/MS +descender +describable/I +describe/BZGDR +describer/M +description/SM +descriptive/PY +descriptiveness/M +descriptor/S +descry/GDS +desecrate/DSGN +desecration/M +deselection +desert/SDRZGM +deserter/M +desertification +desertion/SM +deserved/UY +deserving/U +desiccant/SM +desiccate/DSGN +desiccation/M +desiccator/SM +desiderata +desideratum/M +design/ASDG +designate/DSGNX +designation/M +desirability/UM +desirableness/M +desirably/U +desire/B +desired/U +desirous +desist/SDG +desk/SM +deskill/G +desktop/SM +desolate/PDSYGN +desolateness/M +desolation/M +despair/SMDG +despairing/Y +desperado/M +desperadoes +desperate/YNP +desperateness/M +desperation/M +despicable +despicably +despise/DSG +despite +despoilment/M +despondence/M +despondency/M +despondent/Y +despotic +despotically +despotism/M +dessert/SM +dessertspoon/S +dessertspoonful/S +destination/SM +destine/DSG +destiny/SM +destitute/N +destitution/M +destroy/SZGDR +destroyer/M +destruct/GVMDS +destructibility/IM +destructible/I +destruction/M +destructive/PY +destructiveness/M +desuetude/M +desultorily +desultory +detach/BLGDS +detachment/MS +detain/LGDS +detainee/MS +detainment/M +detect/SDGVB +detectable/U +detected/U +detection/M +detective/SM +detector/SM +detente/SMNX +detention/M +deter/SL +detergent/SM +deteriorate/DSGN +deterioration/M +determent/M +determinable/I +determinant/SM +determinate +determine/AGDS +determined/U +determinedly +determiner/SM +determinism/M +deterministic +deterministically +deterred/U +deterrence/M +deterrent/MS +deterring +detestably +detestation/M +dethrone/DSLG +dethronement/M +detonate/GNDSX +detonation/M +detonator/SM +detox/MDSG +detoxification/M +detoxify/DSGN +detract/GD +detriment/SM +detrimental/Y +detritus/M +deuce/SM +deuterium/M +devastate/GNDS +devastating/Y +devastation/M +devastator/MS +develop/ASGDL +developed/U +developer/SM +development/ASM +developmental/Y +deviance/M +deviancy/M +deviant/SM +deviate/DSMGNX +deviating/U +deviation/M +devil/SMDGL +devilish/YP +devilishness/M +devilment/M +devilry/SM +deviltry/SM +devious/YP +deviousness/M +devoid +devolution/M +devolve/DSG +devoted/Y +devotee/SM +devotion/MS +devotional/SM +devour/SDG +devout/PRYT +devoutness/M +dew/M +dewberry/SM +dewclaw/SM +dewdrop/SM +dewiness/M +dewlap/SM +dewy/RTP +dexterity/M +dexterous/YP +dexterousness/M +dextrose/M +dharma +dhoti/SM +dhow/MS +diabetes/M +diabetic/SM +diabolic +diabolical/Y +diacritic/MS +diacritical +diadem/SM +diaereses +diaeresis/M +diagnose/DSG +diagnosis/M +diagnostic/S +diagnostically +diagnostician/SM +diagnostics/M +diagonal/SMY +diagram/SM +diagrammatic +diagrammatically +diagrammed +diagramming +dial/AMDGS +dialect/SM +dialectal +dialectic/SM +dialectical +dialectics/M +dialing/S +dialog +dialogue/SM +dialyses +dialysis/M +dialyzes +diam +diamagnetic +diamagnetism +diamante +diameter/SM +diametric +diametrical/Y +diamond/SM +diamondback/MS +diapason/SM +diaper/SMDG +diaphanous +diaphragm/SM +diaphragmatic +diarist/SM +diarrhea/M +diary/SM +diaspora/SM +diastase/M +diastole/M +diastolic +diathermy/M +diatom/SM +diatomic +diatonic +diatribe/SM +diazepam +dibble/DSMG +dibs/M +dice/GDS +dices/I +dicey +dichotomous +dichotomy/SM +dicier +diciest +dick/MRXZS +dicker/DG +dickey/SM +dickhead/S +dickybird/S +dicotyledon/MS +dicotyledonous +dict +dicta +dictate/DSMGNX +dictation/M +dictator/SM +dictatorial/Y +dictatorship/SM +diction/M +dictionary/SM +dictum/M +did/AU +didactic +didactically +diddle/DRSZG +diddler/M +diddly +diddlysquat +diddums +didgeridoo/S +didn't +dido/M +didoes +didst +die/DSM +dielectric/MS +diereses +dieresis/M +diesel/SMDG +diet/MDRZGS +dietary/SM +dieter/M +dietetic/S +dietetics/M +dietitian/MS +diff/DRZGS +differ/DG +difference/IM +differences +different/IY +differentiable +differential/SM +differentiate/DSGN +differentiated/U +differentiation/M +difficult/Y +difficulty/SM +diffidence/M +diffident/Y +diffract/GSD +diffraction/M +diffuse/DSYGNVP +diffuseness/M +diffusion/M +diffusivity +dig/SM +digerati/M +digest/SMDGV +digested/U +digestibility/M +digestible/I +digestion/IM +digestions +digestive/S +digger/SM +digging/S +diggings/M +digicam/S +digit/SM +digital/Y +digitalis/M +digitization +digitize/GDS +dignified/U +dignify/DSG +dignitary/SM +dignity/ISM +digraph/M +digraphs +digress/GVDS +digression/MS +dike/MGDS +diktat/S +dilapidated +dilapidation/M +dilatation/M +dilate/DSGN +dilation/M +dilator/SM +dilatory +dildo/S +dilemma/MS +dilettante/SM +dilettantish +dilettantism/M +diligence/M +diligent/Y +dill/MS +dilly/SM +dillydally/DSG +diluent +dilute/DSGNX +diluted/U +dilution/M +dim/PSRY +dime/MS +dimension/SM +dimensional +dimensionless +diminish/GDS +diminished/U +diminuendo/SM +diminution/SM +diminutive/SM +dimity/M +dimmed/U +dimmer/SM +dimmest +dimming +dimness/M +dimple/DSMG +dimply +dimwit/SM +dimwitted +din/ZGSMDR +dinar/SM +dine/S +diner/M +dinette/MS +ding/MDG +dingbat/MS +dinghy/SM +dingily +dinginess/M +dingle/SM +dingo/M +dingoes +dingus/MS +dingy/RPT +dink/R +dinky/RSMT +dinned +dinner/SMDG +dinnertime/M +dinnerware/M +dinning +dinosaur/SM +dint/M +diocesan/MS +diocese/MS +diode/SM +diorama/SM +dioxide/SM +dioxin/SM +dip/SM +diphtheria/M +diphthong/SM +diploid/SM +diploma/SM +diplomacy/M +diplomat/MS +diplomata +diplomatic/U +diplomatically +diplomatist/MS +diplopia +dipole/SM +dipped +dipper/SM +dipping +dippy/RT +dipso/S +dipsomania/M +dipsomaniac/MS +dipstick/SM +dipterous +diptych/M +diptychs +dire/YTR +direct/ASDGVT +directer +direction/IM +directional +directionless +directions +directive/SM +directly +directness/IM +director/MS +directorate/SM +directorial +directorship/SM +directory/SM +direful +dirge/SM +dirigible/MS +dirk/MS +dirndl/SM +dirt/M +dirtball/S +dirtily +dirtiness/M +dirty/DRSTGP +dis/M +disable/DSGL +disablement/M +disambiguate/N +disappointing/Y +disarming/Y +disassembly +disastrous/Y +disbandment/M +disbarment/M +disbelieving/Y +disbursal/M +disburse/DSGL +disbursement/MS +disc/M +discern/LSDG +discernible/I +discernibly +discerning/Y +discernment/M +discharged/U +disciple/SM +discipleship/M +disciplinarian/SM +disciplinary +discipline/DSMG +disciplined/U +disclose/DSG +disclosed/U +disco/MG +discography/SM +discoloration/S +discombobulate/DSGN +discombobulation/M +discomfit/DG +discomfiture/M +discommode/DG +disconcerting/Y +disconnected/PY +disconnectedness/M +disconsolate/Y +discordance/M +discordant/Y +discotheque/SM +discourage/LGDS +discouragement/SM +discouraging/Y +discover/ASDG +discovered/U +discoverer/MS +discovery/ASM +discreet/PRYT +discreetness/M +discrepancy/SM +discrepant +discrete/PYN +discreteness/M +discretion/IM +discretionary +discriminant +discriminate/GNDS +discriminating/U +discrimination/M +discriminator/MS +discriminatory +discursiveness/M +discus/MS +discussant/SM +discussion/SM +disdain/SMDG +disdainful/Y +disembowel/SDLG +disembowelment/M +disfigurement/SM +disfranchisement/M +disgorgement/M +disgruntle/LGDS +disgruntlement/M +disguise/GD +disguised/U +disgusted/Y +disgusting/Y +dish/MDSG +dishabille/M +disharmonious +dishcloth/M +dishcloths +disheartening/Y +dishevel/DGLS +dishevelment/M +dishpan/SM +dishrag/SM +dishtowel/MS +dishware/M +dishwasher/MS +dishwater/M +dishy +disillusion/GLD +disillusionment/M +disinfectant/MS +disinfection/M +disinterested/PY +disinterestedness/M +disjointed/YP +disjointedness/M +disjunctive +disjuncture +disk/MS +diskette/MS +dislodge/GDS +dismal/Y +dismantlement/M +dismay/SMDG +dismayed/U +dismember/LGD +dismemberment/M +dismissive/Y +disorder/Y +disorganization/M +disparage/DSGL +disparagement/M +disparaging/Y +disparate/Y +dispatcher/MS +dispel/S +dispelled +dispelling +dispensary/SM +dispensation/MS +dispense/BZGDRS +dispenser/M +dispersal/M +disperse/GNDS +dispersion/M +dispirit/GDS +displeasure/M +disposable/SM +disposal/SM +disposed/I +disposition/ISM +dispossession/M +disproof/SM +disproportional +disprove/B +disputable/I +disputably/I +disputant/MS +disputation/SM +disputatious/Y +dispute/DRSMZGB +disputed/U +disputer/M +disquiet/GSMD +disquisition/MS +disregardful +disrepair/M +disrepute/MB +disrupt/GVSD +disruption/SM +disruptive/Y +dissect/SDG +dissed +dissemblance/M +dissemble/ZGDRS +dissembler/M +disseminate/GNDS +dissemination/M +dissension/SM +dissent/SMDRZG +dissenter/M +dissertation/SM +disses +dissidence/M +dissident/MS +dissimilar +dissimilitude/S +dissing +dissipate/GNDS +dissipation/M +dissociate/GNVDS +dissociation/M +dissoluble/I +dissolute/YNP +dissoluteness/M +dissolve/AGDS +dissolved/U +dissonance/SM +dissonant +dissuade/GDS +dissuasive +dist +distaff/SM +distal/Y +distance/DSMG +distant/Y +distaste/SM +distemper/M +distention/SM +distillate/SMNX +distillation/M +distillery/SM +distinct/IYTVP +distincter +distinction/SM +distinctive/YP +distinctiveness/M +distinctness/IM +distinguish/GDSB +distinguishable/I +distinguished/U +distort/GDR +distortion/MS +distract/DG +distracted/Y +distraction/S +distrait +distraught +distress/DG +distressful +distressing/Y +distribute/AGNVDS +distributed/U +distribution/AM +distributional +distributions +distributive/Y +distributor's +distributor/AS +distributorship/S +district's +district/AS +disturb/ZGSDR +disturbance/SM +disturbed/U +disturber/M +disturbing/Y +disunion/M +disyllabic +ditch/MDSG +dither/SMDRZG +ditherer/M +ditransitive +ditsy +ditto/SMDG +ditty/SM +ditz/MS +diuretic/MS +diurnal/Y +div +diva/MS +divalent +divan/SM +dive/MZTGDRS +diver/M +diverge/DSG +divergence/MS +divergent +diverse/XYNP +diverseness/M +diversification/M +diversify/GNDS +diversion/M +diversionary +diversity/SM +divert/SDG +diverticulitis/M +divest/SLDG +divestiture/MS +divestment/M +divide/DRSMZGB +divided/U +dividend/MS +divider/M +divination/M +divine/DRSMYZTG +diviner/M +diving/M +divinity/SM +divisibility/IM +divisible/I +division/MS +divisional +divisive/PY +divisiveness/M +divisor/SM +divorce/DSLMG +divorcee/MS +divorcement/MS +divot/SM +divulge/GDS +divvy/DSMG +dixieland/M +dizzily +dizziness/M +dizzy/DRSPTG +djellaba/MS +do/SJMRHZG +doable +dob/S +dobbed +dobbin/SM +dobbing +doberman/MS +dobro +doc/SM +docent/SM +docile/Y +docility/M +dock/MDRZGS +docket/SMDG +dockland/S +dockside +dockworker/MS +dockyard/MS +doctor/SMDG +doctoral +doctorate/MS +doctrinaire/MS +doctrinal +doctrine/MS +docudrama/SM +document/GMDS +documentary/SM +documentation/SM +documented/U +dodder/SMDG +doddery +doddle +dodge/DRSMZG +dodgem/S +dodger/M +dodgy/RT +dodo/MS +doe/SM +doer/M +does/AU +doeskin/MS +doesn't +doff/DGS +dog/SM +dogcart/SM +dogcatcher/SM +doge/MS +dogeared +dogfight/SM +dogfish/MS +dogged/PY +doggedness/M +doggerel/M +dogging +doggone/TGRS +doggy/RSMT +doghouse/SM +dogie/SM +dogleg/SM +doglegged +doglegging +doglike +dogma/SM +dogmatic +dogmatically +dogmatism/M +dogmatist/SM +dognapper +dogsbody/S +dogsled/S +dogtrot/MS +dogtrotted +dogtrotting +dogwood/MS +doily/SM +doing/USM +doldrums/M +dole's +dole/FGDS +doleful/YP +dolefulness/M +doll/MDGS +dollar/SM +dollhouse/SM +dollop/SGMD +dolly/SM +dolmen/SM +dolomite/M +dolor/M +dolorous/Y +dolphin/MS +dolt/MS +doltish/YP +doltishness/M +domain/SM +dome/MGDS +domestic/SM +domestically +domesticate/DSGN +domesticated/U +domestication/M +domesticity/M +domicile/DSMG +domiciliary +dominance/M +dominant/SMY +dominate/DSGN +domination/M +dominatrices +dominatrix/M +domineer/SGD +domineering/Y +dominion/SM +domino/M +dominoes +don't +don/SM +dona/MS +donate/DSXGN +donation/M +done/FAU +dong/MDGS +dongle/SM +donkey/SM +donned +donning +donnish +donnybrook/MS +donor/SM +donuts +doodad/SM +doodah +doodahs +doodle/DRSMZG +doodlebug/SM +doodler/M +doohickey/SM +doolally +doom/MDGS +doomsayer/MS +doomsday/M +doomster/S +door's +door/IS +doorbell/MS +doorjamb/S +doorkeeper/MS +doorknob/MS +doorknocker/S +doorman/M +doormat/SM +doormen +doorplate/SM +doorpost/S +doorstep/MS +doorstepped +doorstepping +doorstop/MS +doorway/SM +dooryard/MS +dopa/M +dopamine +dope/MZGDRS +doper/M +dopey +dopier +dopiest +dopiness/M +doping/M +doppelganger/S +dork/MS +dorky/RT +dorm/MRZS +dormancy/M +dormant +dormer/M +dormice +dormitory/SM +dormouse/M +dorsal/Y +dory/SM +dosage/SM +dose/MGDS +dosh +dosimeter/SM +doss/DRSZG +dosshouse/S +dossier/MS +dost +dot/ZGSMDR +dotage/M +dotard/SM +dotcom/SM +dote/S +doter/M +doting/Y +dotted +dotting +dotty/RT +double's +double/ADSG +doubleheader/MS +doublespeak/M +doublet/MS +doubloon/SM +doubly +doubt/SMDRZG +doubter/M +doubtful/PY +doubtfulness/M +doubting/Y +doubtless/Y +douche/DSMG +dough/M +doughnut/SM +doughty/RT +doughy/TR +dour/RYTP +dourness/M +douse/DSG +dove/MS +dovecot/S +dovecote/SM +dovetail/MDSG +dovish +dowager/MS +dowdily +dowdiness/M +dowdy/RSPT +dowel/SMDG +dower/SMDG +down/MDRZGS +downbeat/SM +downcast +downdraft/MS +downer/M +downfall/SMN +downfield +downgrade/DSMG +downhearted/PY +downheartedness/M +downhill/MS +download/MDBSG +downmarket +downplay/DSG +downpour/MS +downrange +downright +downriver +downscale +downshift/SGD +downside/MS +downsize/GDS +downsizing/M +downspout/MS +downstage +downstairs/M +downstate/M +downstream +downswing/MS +downtempo +downtime/M +downtown/M +downtrend/MS +downtrodden +downturn/MS +downward/S +downwind +downy/RT +dowry/SM +dowse/DRSZG +dowser/M +doxology/SM +doyen/SM +doyenne/MS +doz/XGDNS +doze/M +dozen/MH +dozily +dozy/RTP +dpi +dpt +drab/MYSP +drabber +drabbest +drabness/M +drachma/MS +draconian +draft's +draft/ASDG +draftee/SM +drafter/SM +draftily +draftiness/M +drafting/M +draftsman/M +draftsmanship/M +draftsmen +draftswoman/M +draftswomen +drafty/RTP +drag/MS +dragged +dragging +draggy/TR +dragnet/SM +dragon/SM +dragonfly/SM +dragoon/SMDG +dragster/S +drain/SMDRZG +drainage/M +drainboard/SM +drainer/M +drainpipe/MS +drake/SM +dram/MS +drama/SM +dramatic/S +dramatically +dramatics/M +dramatist/SM +dramatization/SM +dramatize/DSG +drank +drape/DRSMZG +draper/M +drapery/SM +drastic +drastically +drat +dratted +draughtboard/S +draw/MRZGSJ +drawback/MS +drawbridge/MS +drawer/M +drawing/M +drawl/SMDG +drawn/A +drawstring/MS +dray/MS +dread/SMDG +dreadful/PY +dreadfulness/M +dreadlocks/M +dreadnought/MS +dream/SMDRZG +dreamboat/MS +dreamed/U +dreamer/M +dreamily +dreaminess/M +dreamland/M +dreamless +dreamlike +dreamworld/SM +dreamy/RPT +drear +drearily +dreariness/M +dreary/RPT +dredge/DRSMZG +dredger/M +dregs/M +drench/GDS +dress/AUGSDM +dressage/M +dresser/MS +dressiness/M +dressing/SM +dressmaker/SM +dressmaking/M +dressy/TPR +drew/A +dribble/MZGDRS +dribbler/M +driblet/MS +drier/M +drift/SMDRZG +drifter/M +driftnet/S +driftwood/M +drill/SMDRZG +driller/M +drillmaster/SM +drink/SMRBJZG +drinkable/U +drinker/M +drip/MS +dripped +dripping/SM +drippy/TR +drive/RSMZGJ +drivel/SZGMDR +driveler/M +driven +driver/M +driveshaft/SM +driveway/MS +drizzle/MGDS +drizzly +drogue/SM +droid/S +droll/RPT +drollery/SM +drollness/M +drolly +dromedary/SM +drone/DSMG +drool/SMDG +droop/GSMD +droopiness/M +droopy/TPR +drop/MS +dropkick/MS +droplet/SM +dropout/SM +dropped +dropper/SM +dropping/S +droppings/M +dropsical +dropsy/M +dross/M +drought/SM +drove/RSMZ +drover/M +drown/GSJD +drowning/M +drowse/MGDS +drowsily +drowsiness/M +drowsy/RTP +drub/S +drubbed +drubber/SM +drubbing/MS +drudge/MGDS +drudgery/M +drug/MS +drugged +druggie/SM +drugging +druggist/SM +druggy +drugstore/MS +druid/SM +druidism/M +drum/MS +drumbeat/SM +drumlin/SM +drummed +drummer/SM +drumming +drumstick/SM +drunk/STMNR +drunkard/MS +drunken/PY +drunkenness/M +drupe/SM +druthers/M +dry/ZTGDRSMY +dryad/SM +dryer/SM +dryness/M +drys +drywall/M +dual +dualism/M +duality/M +dub/SM +dubbed +dubber/SM +dubbin/M +dubbing +dubiety/M +dubious/YP +dubiousness/M +ducal +ducat/SM +duchess/MS +duchy/SM +duck/MDGS +duckbill/SM +duckboards +duckling/SM +duckpins/M +duckweed/M +ducky/TRSM +duct's/K +duct/CKIFS +ductile +ductility/M +ducting +ductless +dud/GSMD +dude/MS +dudgeon/M +due/SM +duel/MDRJZGS +dueler/M +duelist/SM +duenna/MS +duet/MS +duff/MDRZGS +duffer/M +dug +dugout/MS +duh +duke/MS +dukedom/SM +dulcet +dulcimer/MS +dull/DRPTGS +dullard/SM +dullness/M +dully +duly/U +dumb/RYPT +dumbbell/SM +dumbfound/SDG +dumbness/M +dumbo/S +dumbstruck +dumbwaiter/SM +dumdum/MS +dummy/SM +dump/MDRZGS +dumpiness/M +dumpling/SM +dumpsite/S +dumpster/SM +dumpy/PTR +dun/SM +dunce/SM +dunderhead/MS +dune/MS +dung/MDGS +dungaree/MS +dungeon/SM +dunghill/MS +dunk/MDGS +dunned +dunner +dunnest +dunning +dunno +duo/SM +duodecimal +duodena +duodenal +duodenum/M +duopoly/S +dupe/MZGDRS +duper/M +duple +duplex/MS +duplicate's +duplicate/AGNDS +duplication/AM +duplicator/MS +duplicitous +duplicity/M +durability/M +durable +durably +durance/M +duration/M +duress/M +during +durst +durum/M +dusk/M +duskiness/M +dusky/RTP +dust/MDRZGS +dustbin/SM +dustcart/S +duster/M +dustiness/M +dustless +dustman +dustmen +dustpan/SM +dustsheet/S +dusty/RTP +dutch +duteous/Y +dutiable +dutiful/YP +dutifulness/M +duty/SM +duvet/SM +dwarf/SGMD +dwarfish +dwarfism/M +dweeb/SM +dwell/SJZGR +dweller/M +dwelling/M +dwelt/I +dwindle/DSG +dyadic +dybbuk/SM +dybbukim +dye/DRSMZG +dyeing/A +dyer/M +dyestuff/M +dying/M +dyke/MS +dynamic/MS +dynamical/Y +dynamics/M +dynamism/M +dynamite/MZGDRS +dynamiter/M +dynamo/SM +dynastic +dynasty/SM +dysentery/M +dysfunction/MS +dysfunctional +dyslectic/SM +dyslexia/M +dyslexic/SM +dyspepsia/M +dyspeptic/MS +dysphagia +dysphoria +dysphoric +dysprosium/M +dystonia +dystopi +dystopia +dystopian +dz +e'en +e'er +e/FDST +eBay/M +eMusic/M +ea +each +eager/PTRY +eagerness/M +eagle/MS +eaglet/MS +ear/SMDY +earache/SM +earbud/SM +eardrum/SM +earful/SM +earl/MS +earldom/SM +earliness/M +earlobe/SM +early/RTP +earmark/SMDG +earmuff/SM +earn/DRZTGJS +earned/U +earner/M +earnest/SMYP +earnestness/M +earnings/M +earphone/MS +earpiece/S +earplug/SM +earring/SM +earshot/M +earsplitting +earth's +earth/UDYG +earthbound +earthen +earthenware/M +earthiness/M +earthling/MS +earthly/RT +earthquake/SM +earths/U +earthshaking +earthward/S +earthwork/MS +earthworm/MS +earthy/RTP +earwax/M +earwig/SM +ease/EDSM +easel/SM +easement/SM +easily/U +easiness/UM +easing +east/M +eastbound +easterly/SM +eastern/ZR +easterner/M +easternmost +eastward/S +easy/URTP +easygoing +eat/ZGBSNR +eatable/SM +eaten/U +eater/M +eatery/SM +eave/MS +eavesdrop/S +eavesdropped +eavesdropper/SM +eavesdropping +ebb/SMDG +ebony/SM +ebullience/M +ebullient/Y +ebullition/M +eccentric/SM +eccentrically +eccentricity/SM +eccl +ecclesial +ecclesiastic/SM +ecclesiastical/Y +echelon/SM +echidna +echinoderm/SM +echo's +echo/ADG +echoes/A +echoic +echolocation/M +echos +eclair/SM +eclat/M +eclectic/SM +eclectically +eclecticism/M +eclipse/DSMG +ecliptic/M +eclogue/SM +ecocide/M +ecol +ecologic +ecological/Y +ecologist/MS +ecology/M +econ +econometric/S +economic/S +economical/UY +economics/M +economist/SM +economize/DRSZG +economizer/M +economy/SM +ecosystem/MS +ecotourism/M +ecotourist/MS +ecru/M +ecstasy/SM +ecstatic +ecstatically +ecu/S +ecumenical/Y +ecumenicism/M +ecumenism/M +eczema/M +ed/ACSM +edamame +eddy/DSMG +edelweiss/M +edema/SM +edge/MZGJDRS +edger/M +edgewise +edgily +edginess/M +edging/M +edgy/RTP +edibility/M +edible/SMP +edibleness/M +edict/SM +edification/M +edifice/SM +edifier/M +edify/DRSZGN +edifying/U +edit's +edit/ADGS +editable +edited/U +edition/MS +editor/SM +editorial/SMY +editorialize/DSG +editorship/M +educ +educability/M +educable/I +educate/ADSGNV +educated/U +education/AM +educational/Y +educationalist/S +educationist/S +educations +educator/MS +educe/DSGB +edutainment/M +eek +eel/SM +eerie/RT +eerily +eeriness/M +eff/GSD +efface/DSLG +effacement/M +effect/SMDGV +effective/IPY +effectiveness/IM +effectual/IY +effectuate/DSG +effeminacy/M +effeminate/Y +effendi/SM +efferent +effervesce/GDS +effervescence/M +effervescent/Y +effete/YP +effeteness/M +efficacious/Y +efficacy/IM +efficiency/ISM +efficient/IY +effigy/SM +efflorescence/M +efflorescent +effluence/M +effluent/MS +effluvia +effluvium/M +efflux +effort/SM +effortful +effortless/YP +effortlessness/M +effrontery/M +effulgence/M +effulgent +effuse/DSGNVX +effusion/M +effusive/YP +effusiveness/M +egad +egalitarian/SM +egalitarianism/M +egg/GSMD +eggbeater/MS +eggcup/SM +egghead/SM +eggnog/M +eggplant/MS +eggshell/SM +eglantine/SM +ego/SM +egocentric/MS +egocentrically +egocentricity/M +egoism/M +egoist/SM +egoistic +egoistical/Y +egomania/M +egomaniac/MS +egotism/M +egotist/SM +egotistic +egotistical/Y +egregious/PY +egregiousness/M +egress/MS +egret/SM +eh +eider/SM +eiderdown/MS +eigenvalue/S +eigenvector/S +eight/SM +eighteen/MHS +eighteenth/M +eighteenths +eighth/M +eighths +eightieth/M +eightieths +eighty/SMH +einsteinium/M +eisteddfod/S +either +ejaculate/GNXDS +ejaculation/M +ejaculatory +eject/SDG +ejection/MS +ejector/SM +eke/DSG +elaborate/YGNDSPX +elaborateness/M +elaboration/M +elan/M +eland/SM +elapse/DSG +elastic/MS +elastically +elasticated +elasticity/M +elasticize/DSG +elate/DSGN +elated/Y +elation/M +elbow/SMDG +elbowroom/M +elder/SMY +elderberry/SM +eldercare/M +eldest +eldritch +elect's +elect/ASDGV +electable +election/AMS +electioneer/DGS +elective/MS +elector/MS +electoral/Y +electorate/MS +electric/S +electrical/Y +electrician/MS +electricity/M +electrification/M +electrifier/M +electrify/ZGNDRS +electrocardiogram/MS +electrocardiograph/M +electrocardiographs +electrocardiography/M +electrocute/DSXGN +electrocution/M +electrode/SM +electrodynamics +electroencephalogram/MS +electroencephalograph/M +electroencephalographic +electroencephalographs +electroencephalography/M +electrologist/SM +electrolysis/M +electrolyte/MS +electrolytic +electromagnet/MS +electromagnetic +electromagnetically +electromagnetism/M +electromotive +electron/MS +electronic/S +electronica/M +electronically +electronics/M +electroplate/DSG +electroscope/SM +electroscopic +electroshock/M +electrostatic/S +electrostatics/M +electrotype/MS +electroweak +eleemosynary +elegance/IM +elegant/IY +elegiac/MS +elegiacal +elegy/SM +elem +element/MS +elemental/Y +elementary +elephant/SM +elephantiasis/M +elephantine +elev +elevate/XDSGN +elevation/M +elevator/MS +eleven/SMH +elevens/S +eleventh/M +elevenths +elf/M +elfin +elfish +elicit/SDG +elicitation/M +elide/DSG +eligibility/IM +eligible +eliminate/XDSGN +elimination/M +eliminator/S +elision/MS +elite/SM +elitism/M +elitist/MS +elixir/SM +elk/SM +ell/SM +ellipse/MS +ellipsis/M +ellipsoid/SM +ellipsoidal +elliptic +elliptical/Y +elm/SM +elocution/M +elocutionary +elocutionist/SM +elodea/SM +elongate/DSGNX +elongation/M +elope/DSGL +elopement/MS +eloquence/M +eloquent/Y +else +elsewhere +elucidate/DSGNX +elucidation/M +elude/DSG +elusive/YP +elusiveness/M +elver/SM +elves +elvish +em's +em/S +emaciate/GNDS +emaciation/M +email/SMDG +emanate/XDSGN +emanation/M +emancipate/DSGN +emancipation/M +emancipator/MS +emasculate/GNDS +emasculation/M +embalm/SZGDR +embalmer/M +embank/SLGD +embankment/SM +embargo/MDG +embargoes +embark/AEGDS +embarkation/EM +embarkations +embarrass/GLDS +embarrassed/U +embarrassing/Y +embarrassment/SM +embassy/SM +embattled +embed/S +embedded +embedding +embellish/LGDS +embellishment/SM +ember/SM +embezzle/ZGLDRS +embezzlement/M +embezzler/M +embitter/GLDS +embitterment/M +emblazon/GDLS +emblazonment/M +emblem/SM +emblematic +emblematically +embodiment/EM +embody/AEGSD +embolden/DGS +embolism/MS +embolization +emboss/DRSZG +embosser/M +embouchure/M +embower/SGD +embrace/DSMG +embraceable +embrasure/MS +embrocation/MS +embroider/SDRZG +embroiderer/M +embroidery/SM +embroil/DGLS +embroilment/M +embryo/SM +embryological +embryologist/MS +embryology/M +embryonic +emcee/DSM +emceeing +emend/SDG +emendation/MS +emerald/MS +emerge/ADSG +emergence/AM +emergency/SM +emergent +emerita +emeritus +emery/M +emetic/SM +emf/S +emigrant/SM +emigrate/DSXGN +emigration/M +emigre/SM +eminence/MS +eminent/Y +emir/MS +emirate/MS +emissary/SM +emission/SM +emit/S +emitted +emitter/MS +emitting +emo/SM +emoji/SM +emollient/MS +emolument/MS +emote/XDSGNV +emoticon/SM +emotion/M +emotional/UY +emotionalism/M +emotionalize/GDS +emotionless +emotive/Y +empathetic +empathically +empathize/DSG +empathy/M +emperor/MS +emphases +emphasis/M +emphasize/AGDS +emphatic/U +emphatically +emphysema/M +empire/SM +empiric +empirical/Y +empiricism/M +empiricist/SM +emplacement/SM +employ's +employ/ADGLS +employable/U +employee/SM +employer/SM +employment/UAM +employments +emporium/SM +empower/SDGL +empowerment/M +empress/MS +emptily +emptiness/M +empty/TGPDRSM +empyrean/M +emu/SM +emulate/DSGNVX +emulation/M +emulator/SM +emulsification/M +emulsifier/M +emulsify/NDRSZG +emulsion/MS +en/SM +enable/DRSZG +enabler/M +enact/ASLDG +enactment/ASM +enamel/JSZGMDR +enameler/M +enamelware/M +enamor/SGD +enc +encamp/LSGD +encampment/MS +encapsulate/XGNDS +encapsulation/M +encase/LDSG +encasement/M +encephalitic +encephalitis/M +enchain/DGS +enchant/ELDGS +enchanter/MS +enchanting/Y +enchantment/EM +enchantments +enchantress/MS +enchilada/SM +encipher/SGD +encircle/DSGL +encirclement/M +encl +enclave/MS +enclose/GDS +enclosed/U +enclosure/SM +encode/DRSZG +encoder/M +encomium/MS +encompass/GDS +encore/DSMG +encounter/GSMD +encourage/DSLG +encouragement/SM +encouraging/Y +encroach/GLDS +encroachment/SM +encrust/DGS +encrustation/SM +encrypt/DGS +encryption +encumber/EGSD +encumbered/U +encumbrance/MS +ency +encyclical/SM +encyclopedia/MS +encyclopedic +encyst/LSGD +encystment/M +end/GVSJMD +endanger/SGDL +endangerment/M +endear/SGLD +endearing/Y +endearment/SM +endeavor/GSMD +endemic/MS +endemically +endgame/S +ending/M +endive/SM +endless/PY +endlessness/M +endmost +endocarditis +endocrine/MS +endocrinologist/MS +endocrinology/M +endogenous/Y +endometrial +endometriosis +endometrium +endorphin/MS +endorse/LZGDRS +endorsement/MS +endorser/M +endoscope/MS +endoscopic +endoscopy/M +endothelial +endothermic +endotracheal +endow/SDLG +endowment/MS +endpoint/SM +endue/DSG +endurable/U +endurance/M +endure/DSBG +endways +enema/SM +enemy/SM +energetic +energetically +energize/ZGDRS +energizer/M +energy/SM +enervate/GNDS +enervation/M +enfeeble/GDSL +enfeeblement/M +enfilade/DSMG +enfold/SGD +enforce/LZGDRS +enforceable/U +enforced/U +enforcement/M +enforcer/M +enfranchise/EGDSL +enfranchisement/EM +engage/EADSG +engagement/EMS +engagingly +engender/SGD +engine/SM +engineer/MDGS +engineering/M +engorge/LGDS +engorgement/M +engram/SM +engrave/ZGJDRS +engraver/M +engraving/M +engross/GLDS +engrossment/M +engulf/SLGD +engulfment/M +enhance/LZGDRS +enhancement/SM +enigma/SM +enigmatic +enigmatically +enjambment/SM +enjoin/SGD +enjoy/GBLSD +enjoyably +enjoyment/SM +enlarge/LZGDRS +enlargeable +enlargement/MS +enlarger/M +enlighten/SGLD +enlightened/U +enlightenment/M +enlist/ADGSL +enlistee/SM +enlistment/AM +enlistments +enliven/SLDG +enlivenment/M +enmesh/DSGL +enmeshment/M +enmity/SM +ennoble/DSGL +ennoblement/M +ennui/M +enormity/SM +enormous/PY +enormousness/M +enough/M +enplane/DSG +enqueue/DS +enquirer/S +enquiringly +enrage/GDS +enrapture/DSG +enrich/DSLG +enrichment/M +enroll/DLSG +enrollment/MS +ensconce/DSG +ensemble/SM +enshrine/GLDS +enshrinement/M +enshroud/DGS +ensign/MS +ensilage/M +enslave/DSGL +enslavement/M +ensnare/DSLG +ensnarement/M +ensue/DSG +ensure/ZGDRS +ensurer/M +entail/DSGL +entailment/M +entangle/EDSLG +entanglement/EM +entanglements +entente/SM +enter/ASGD +enteral +enteric +enteritis/M +enterprise/MGS +enterprising/Y +entertain/ZGDRSL +entertainer/M +entertaining/MY +entertainment/MS +enthrall/GDSL +enthrallment/M +enthrone/GDSL +enthronement/SM +enthuse/DSG +enthusiasm/MS +enthusiast/MS +enthusiastic/U +enthusiastically +entice/GDSL +enticement/MS +enticing/Y +entire/Y +entirety/M +entitle/DSGL +entitlement/SM +entity/SM +entomb/DSGL +entombment/M +entomological +entomologist/MS +entomology/M +entourage/SM +entr'acte +entrails/M +entrained +entrance/LDSMG +entrancement/M +entrancing/Y +entrant/SM +entrap/LS +entrapment/M +entrapped +entrapping +entreat/GSD +entreating/Y +entreaty/SM +entree/MS +entrench/DSGL +entrenchment/MS +entrepreneur/SM +entrepreneurial +entrepreneurship +entropy/M +entrust/SGD +entry/ASM +entryphone/S +entryway/MS +entwine/DSG +enumerable +enumerate/DSGNX +enumeration/M +enumerator/SM +enunciate/DSGN +enunciation/M +enuresis/M +envelop/SLDRZG +envelope/SM +enveloper/M +envelopment/M +envenom/SDG +enviable/U +enviably +envious/PY +enviousness/M +environment/MS +environmental/Y +environmentalism/M +environmentalist/SM +environs/M +envisage/GDS +envision/DGS +envoy/SM +envy/DSMG +envying/Y +enzymatic +enzyme/SM +eolian +eon/SM +eosinophil/S +eosinophilic +epaulet/SM +epee/MS +ephedrine/M +ephemera/M +ephemeral/Y +epic/MS +epicenter/MS +epicure/SM +epicurean/MS +epidemic/SM +epidemically +epidemiological +epidemiologist/SM +epidemiology/M +epidermal +epidermic +epidermis/MS +epidural/S +epiglottis/MS +epigram/SM +epigrammatic +epigraph/M +epigraphs +epigraphy/M +epilepsy/M +epileptic/SM +epilogue/MS +epinephrine/M +epiphany/SM +episcopacy/M +episcopal +episcopate/M +episode/SM +episodic +episodically +epistemic +epistemological +epistemology +epistle/SM +epistolary +epitaph/M +epitaphs +epithelial +epithelium/M +epithet/SM +epitome/SM +epitomize/GDS +epoch/M +epochal +epochs +eponymous +epoxy/DSMG +epsilon/SM +equability/M +equable +equably +equal/SMDYG +equality/IM +equalization/M +equalize/ZGDRS +equalizer/M +equanimity/M +equate/DSGNBX +equation/M +equator/SM +equatorial +equerry/SM +equestrian/SM +equestrianism/M +equestrienne/SM +equidistant/Y +equilateral/SM +equilibrium/EM +equine/SM +equinoctial +equinox/MS +equip/AS +equipage/MS +equipment/M +equipoise/M +equipped/UA +equipping/A +equitable/I +equitably/I +equitation/M +equity/ISM +equiv +equivalence/MS +equivalency/SM +equivalent/MYS +equivocal/UY +equivocalness/M +equivocate/GNXDS +equivocation/M +equivocator/SM +er/C +era/SM +eradicable/I +eradicate/DSGN +eradication/M +eradicator/MS +erase/DRSBZG +eraser/M +erasure/SM +erbium/M +ere +erect/PSGDY +erectile +erection/SM +erectness/M +erector/MS +erelong +eremite/MS +erg/SM +ergo +ergonomic/S +ergonomically +ergonomics/M +ergosterol/M +ergot/M +ermine/SM +erode/DSG +erodible +erogenous +erosion/M +erosive +erotic/S +erotica/M +erotically +eroticism/M +err/GSD +errand/SM +errant/I +errata/SM +erratic +erratically +erratum/M +erroneous/Y +error/SM +ersatz/MS +erst +erstwhile +eruct/SDG +eructation/SM +erudite/YN +erudition/M +erupt/SDGV +eruption/MS +erysipelas/M +erythrocyte/SM +erythromycin +escalate/CDSGN +escalation/CM +escalations +escalator/MS +escallop/SGMD +escalope/S +escapade/MS +escape/LMGDS +escapee/MS +escapement/SM +escapism/M +escapist/MS +escapologist/S +escapology +escargot/MS +escarole/MS +escarpment/MS +eschatological +eschatology +eschew/SDG +escort/SMDG +escritoire/MS +escrow/SM +escudo/SM +escutcheon/SM +esophageal +esophagi +esophagus/M +esoteric +esoterically +esp +espadrille/MS +espalier/MDSG +especial/Y +espionage/M +esplanade/MS +espousal/M +espouse/GDS +espresso/MS +esprit/M +espy/DSG +esquire/SM +essay/SMDRZG +essayer/M +essayist/SM +essence/SM +essential/IMS +essentially +establish/AESDGL +establishment/AEM +establishments +estate/SM +esteem/ESMDG +ester/SM +estimable/I +estimate/MGNDSX +estimation/M +estimator/SM +estoppel +estradiol +estrange/LDSG +estrangement/MS +estrogen/MS +estrous +estrus/MS +estuary/SM +eta/SM +etc +etch/DRSZGJ +etcher/M +etching/M +eternal/YP +eternalness/M +eternity/SM +ethane/M +ethanol/M +ether/M +ethereal/Y +ethic/SM +ethical/UY +ethics/M +ethmoid +ethnic/SM +ethnically +ethnicity/M +ethnocentric +ethnocentrism/M +ethnographer/S +ethnographic +ethnographically +ethnography +ethnological/Y +ethnologist/SM +ethnology/M +ethological +ethologist/MS +ethology/M +ethos/M +ethyl/M +ethylene/M +etiolated +etiologic +etiological +etiology/SM +etiquette/M +etude/SM +etymological/Y +etymologist/SM +etymology/SM +eucalypti +eucalyptus/MS +euchre/DSMG +euclidean +eugenic/S +eugenically +eugenicist/MS +eugenics/M +eukaryote/SM +eukaryotic +eulogist/MS +eulogistic +eulogize/ZGDRS +eulogizer/M +eulogy/SM +eunuch/M +eunuchs +euphemism/SM +euphemistic +euphemistically +euphonious/Y +euphony/M +euphoria/M +euphoric +euphorically +eureka +euro/MS +europium/M +eutectic +euthanasia/M +euthanize/DSG +euthenics/M +eutrophication +evacuate/XDSGN +evacuation/M +evacuee/MS +evade/DRSZG +evader/M +evaluate/AGNVDSX +evaluation/AM +evaluator/S +evanescence/M +evanescent +evangelic +evangelical/SMY +evangelicalism/M +evangelism/M +evangelist/MS +evangelistic +evangelize/GDS +evaporate/GNDS +evaporation/M +evaporator/SM +evasion/SM +evasive/YP +evasiveness/M +eve/ASM +even/MDRYTGSJP +evenhanded/Y +evening/M +evenness/UM +evensong/M +event/SM +eventful/UY +eventfulness/M +eventide/M +eventual/Y +eventuality/SM +eventuate/GDS +ever +everglade/SM +evergreen/SM +everlasting/MYS +evermore +every +everybody/M +everyday +everyone/M +everyplace +everything/M +everywhere +evict/SDG +eviction/MS +evidence/MGDS +evident/Y +evil/MRYTSP +evildoer/SM +evildoing/M +eviller +evillest +evilness/M +evince/DSG +eviscerate/DSGN +evisceration/M +evocation/MS +evocative/Y +evoke/DSG +evolution/M +evolutionary +evolutionist/SM +evolve/DSG +ewe/RSMZ +ewer/M +ex/MS +exabyte/MS +exacerbate/GNDS +exacerbation/M +exact/SPDRYTG +exacting/Y +exaction/M +exactitude/M +exactness/IM +exaggerate/XDSGN +exaggerated/Y +exaggeration/M +exaggerator/MS +exajoule/S +exalt/SDG +exaltation/M +exam/MS +examination/AMS +examine/AGDS +examiner/MS +example/MGDS +exampled/U +exasperate/DSGN +exasperated/Y +exasperating/Y +exasperation/M +excavate/GNDSX +excavation/M +excavator/SM +exceed/GSD +exceeding/Y +excel/S +excelled +excellence/M +excellency/SM +excellent/Y +excelling +excelsior/M +except/GSD +exception/BSM +exceptionable/U +exceptional/UY +exceptionalism +excerpt/MDGS +excess/VMS +excessive/Y +exchange/DSMG +exchangeable +exchequer/SM +excise/XDSMGN +excision/M +excitability/M +excitably +excitation/M +excite/BDRSLZG +excited/Y +excitement/SM +exciter/M +exciting/Y +exciton +excl +exclaim/DGS +exclamation/SM +exclamatory +exclude/GDS +exclusion/MS +exclusionary +exclusive/PMYS +exclusiveness/M +exclusivity/M +excommunicate/GNDSX +excommunication/M +excoriate/DSGNX +excoriation/M +excrement/M +excremental +excrescence/MS +excrescent +excreta/M +excrete/XGNDS +excretion/M +excretory +excruciating/Y +exculpate/DSGN +exculpation/M +exculpatory +excursion/MS +excursionist/MS +excursive/YP +excursiveness/M +excusable/I +excusably/I +excuse/DSBMG +excused/U +exec/MS +execrable +execrably +execrate/DSGN +execration/M +execute/BXGNVDS +execution/ZMR +executioner/M +executive/SM +executor/MS +executrices +executrix/M +exegeses +exegesis/M +exegetic +exegetical +exemplar/SM +exemplary +exemplification/M +exemplify/GDSXN +exempt/SGD +exemption/SM +exercise/DRSMZG +exerciser/M +exert/SDG +exertion/MS +exeunt +exfoliate/GNDS +exhalation/MS +exhale/DSG +exhaust/GVMDS +exhaustible/I +exhaustion/M +exhaustive/YP +exhaustiveness/M +exhibit/GMDS +exhibition/MS +exhibitionism/M +exhibitionist/MS +exhibitor/SM +exhilarate/DSGN +exhilaration/M +exhort/SDG +exhortation/MS +exhumation/MS +exhume/DSG +exigence/MS +exigency/SM +exigent +exiguity/M +exiguous +exile/DSMG +exilic +exist/SDG +existence/MS +existent +existential/Y +existentialism/M +existentialist/MS +exit/MDGS +exobiology/M +exodus/MS +exogenous +exon/MS +exonerate/GNDS +exoneration/M +exoplanet/MS +exorbitance/M +exorbitant/Y +exorcise/DSG +exorcism/SM +exorcist/SM +exoskeleton/SM +exosphere/SM +exothermic +exotic/SM +exotica +exotically +exoticism/M +exp +expand/BGSD +expanse/XMNVS +expansible +expansion/M +expansionary +expansionism/M +expansionist/MS +expansive/YP +expansiveness/M +expat/S +expatiate/GNDS +expatiation/M +expatriate/DSMGN +expatriation/M +expect/GSD +expectancy/M +expectant/Y +expectation/SM +expectorant/SM +expectorate/DSGN +expectoration/M +expedience/IM +expediences +expediencies +expediency/IM +expedient/SMY +expedite/DRSZGNX +expediter/M +expedition/M +expeditionary +expeditious/PY +expeditiousness/M +expel/S +expelled +expelling +expend/GSBD +expendable/SM +expenditure/SM +expense/MS +expensive/IYP +expensiveness/IM +experience/IMD +experiences +experiencing +experiential +experiment/MDRSZG +experimental/Y +experimentation/M +experimenter/M +expert/SPMY +expertise/M +expertness/M +expiate/GNDS +expiation/M +expiatory +expiration/M +expire/DSG +expired/U +expiry/M +explain/ADGS +explainable +explained/U +explanation/MS +explanatory +expletive/MS +explicable/I +explicate/XGNDS +explication/M +explicit/PY +explicitness/M +explode/GDS +exploit/ZGBMDRS +exploitation/M +exploitative +exploited/U +exploiter/M +exploration/MS +exploratory +explore/ZGDRS +explored/U +explorer/M +explosion/SM +explosive/SPMY +explosiveness/M +expo/MS +exponent/MS +exponential/Y +exponentiation +export/BSZGMDR +exportation/M +exporter/M +expose/DSMG +exposed/U +exposition/SM +expositor/SM +expository +expostulate/GNXDS +expostulation/M +exposure/MS +expound/ZGDRS +expounder/M +express/GVMDSY +expressed/U +expressible/I +expression/SM +expressionism/M +expressionist/SM +expressionistic +expressionless/Y +expressive/PY +expressiveness/M +expressway/SM +expropriate/GNXDS +expropriation/M +expropriator/SM +expulsion/MS +expunge/GDS +expurgate/DSGNX +expurgated/U +expurgation/M +exquisite/YP +exquisiteness/M +ext +extant +extemporaneous/PY +extemporaneousness/M +extempore +extemporization/M +extemporize/GDS +extend/SZGDRB +extender/M +extensibility +extensible +extension/SM +extensional +extensive/YP +extensiveness/M +extent/SM +extenuate/DSGN +extenuation/M +exterior/MS +exterminate/DSXGN +extermination/M +exterminator/MS +external/MYS +externalization/SM +externalize/DSG +extinct/GDS +extinction/MS +extinguish/ZGBDRS +extinguishable/I +extinguisher/M +extirpate/GNDS +extirpation/M +extol/S +extolled +extolling +extort/SGD +extortion/MRZ +extortionate/Y +extortioner/M +extortionist/MS +extra/SM +extracellular +extract/MDGVS +extraction/SM +extractor/MS +extracurricular +extradite/GNBXDS +extradition/M +extrajudicial +extralegal +extramarital +extramural +extraneous/Y +extraordinaire +extraordinarily +extraordinary +extrapolate/XGNDS +extrapolation/M +extrasensory +extraterrestrial/MS +extraterritorial +extraterritoriality/M +extravagance/MS +extravagant/Y +extravaganza/MS +extravehicular +extreme/PMYTRS +extremeness/M +extremism/M +extremist/MS +extremity/SM +extricable/I +extricate/GNDS +extrication/M +extrinsic +extrinsically +extroversion/M +extrovert/SMD +extrude/GDS +extrusion/SM +extrusive +exuberance/M +exuberant/Y +exudation/M +exude/DSG +exult/SDG +exultant/Y +exultation/M +exurb/SM +exurban +exurbanite/SM +exurbia/M +eye/DSM +eyeball/GMDS +eyebrow/SM +eyedropper/SM +eyeful/SM +eyeglass/MS +eyeing +eyelash/MS +eyeless +eyelet/SM +eyelid/SM +eyeliner/MS +eyeopener/MS +eyeopening +eyepiece/MS +eyesight/M +eyesore/MS +eyestrain/M +eyeteeth +eyetooth/M +eyewash/M +eyewitness/MS +f/CIAVTR +fMRI +fa/M +fab +fable/DSM +fabric/SM +fabricate/DSGNX +fabrication/M +fabricator/SM +fabulous/Y +facade/SM +face's +face/ACSDG +facecloth/M +facecloths +faceless +facepalm/SDG +facet/SMDG +facetious/YP +facetiousness/M +facial/SMY +facile/Y +facilitate/GNDS +facilitation/M +facilitator/MS +facility/SM +facing/SM +facsimile/DSM +facsimileing +fact/MS +faction/SM +factional +factionalism/M +factious +factitious +factoid/SM +factor's +factor/ASDG +factorial/MS +factorization +factorize/GDS +factory/SM +factotum/SM +factual/Y +faculty/SM +fad/GSMD +faddish/P +faddist/MS +faddy/P +fade/MS +fading/U +faerie/SM +faff/DGS +fag/SM +fagged +fagging +faggot/SM +fagot/SMG +faience/M +fail/DGJS +failing/M +faille/M +failure/SM +fain/RT +faint/SMDRYTGP +fainthearted +faintness/M +fair/MRYTGJPS +fairground/MS +fairing/M +fairness/UM +fairway/SM +fairy/SM +fairyland/SM +faith/M +faithful's +faithful/UPY +faithfulness/UM +faithfuls +faithless/PY +faithlessness/M +faiths +fajita/SM +fajitas/M +fake/MZGDRS +faker/M +fakir/SM +falcon/SMRZ +falconer/M +falconry/M +fall/MNGS +fallacious/Y +fallacy/SM +fallback +fallibility/IM +fallible/P +fallibleness/M +fallibly/I +falloff/SM +fallout/M +fallow/SMDG +false/PRYT +falsehood/SM +falseness/M +falsetto/SM +falsie/SM +falsifiable +falsification/M +falsifier/M +falsify/DRSZGNX +falsity/SM +falter/GSJMD +faltering/Y +fame's +fame/D +familial +familiar/MYS +familiarity/UM +familiarization/M +familiarize/GDS +family/SM +famine/SM +famish/DSG +famous/IY +fan/SM +fanatic/SM +fanatical/Y +fanaticism/M +fanboy/SM +fanciable +fancier/M +fanciful/YP +fancifulness/M +fancily +fanciness/M +fancy/DRSMZTGP +fancywork/M +fandango/MS +fandom +fanfare/SM +fang/MDS +fanlight/SM +fanned +fanning +fanny/SM +fantail/MS +fantasia/SM +fantasist/S +fantasize/GDS +fantastic +fantastical/Y +fantasy/DSMG +fanzine/MS +far +farad/SM +faradize/DG +faraway +farce/SM +farcical/Y +fare/MGDS +farewell/SM +farina/M +farinaceous +farm/MDRZGSJ +farmer/M +farmhand/SM +farmhouse/SM +farming/M +farmland/MS +farmstead/MS +farmyard/MS +faro/M +farrago/M +farragoes +farrier/MS +farrow/SMDG +farseeing +farsighted/P +farsightedness/M +fart/MDGS +farther +farthermost +farthest +farthing/SM +fascia/SM +fascicle/SM +fascinate/GNDSX +fascinating/Y +fascination/M +fascism/M +fascist/MS +fascistic +fashion/ZGBMDRS +fashionable/U +fashionably/U +fashioner/M +fashionista/MS +fast/MDRTGSP +fastback/SM +fastball/SM +fasten/UAGDS +fastener/SM +fastening/MS +fastidious/PY +fastidiousness/M +fastness/MS +fat/GSPMD +fatal/Y +fatalism/M +fatalist/SM +fatalistic +fatalistically +fatality/SM +fatback/M +fate/MS +fateful/YP +fatefulness/M +fathead/MDS +father/SGMDY +fatherhood/M +fatherland/MS +fatherless +fathom/SMDGB +fathomable/U +fathomless +fatigue/MDSG +fatigues/M +fatness/M +fatso/S +fatten/SDG +fatter +fattest +fattiness/M +fatty/RSMTP +fatuity/M +fatuous/YP +fatuousness/M +fatwa/SM +faucet/SM +fault/CSMDG +faultfinder/SM +faultfinding/M +faultily +faultiness/M +faultless/PY +faultlessness/M +faulty/PRT +faun/MS +fauna/SM +fauvism/M +fauvist/SM +faux +fave/S +favor/ESMDG +favorable/U +favorably/U +favorite/SM +favoritism/M +fawn/MDRZGS +fawner/M +fax/GMDS +fay/TSMR +faze/GDS +fazed/U +fealty/M +fear/MDGS +fearful/YP +fearfulness/M +fearless/PY +fearlessness/M +fearsome +feasibility/M +feasible/IU +feasibly +feast/SMDRZG +feaster/M +feat/MS +feather/SGMD +featherbedding/M +featherbrained +featherless +featherweight/MS +feathery/TR +feature/DSMG +featureless +febrile +fecal +feces/M +feckless/PY +fecund +fecundate/GNDS +fecundation/M +fecundity/M +fed/SM +federal/SMY +federalism/M +federalist/MS +federalization/M +federalize/GDS +federate/FXDSGN +federation/FM +fedora/SM +fee/SM +feeble/RTP +feebleness/M +feebly +feed/MRZGSJ +feedback/M +feedbag/SM +feeder/M +feeding/M +feedlot/SM +feel/MRZGSJ +feeler/M +feelgood +feeling/MY +feet +feign/SDG +feigned/U +feint/SMDG +feisty/TR +feldspar/M +felicitate/GNXDS +felicitation/M +felicitous/Y +felicity/ISM +feline/SM +fell/MDRZTGS +fella/S +fellatio/M +fellow/SM +fellowman/M +fellowmen +fellowship/MS +felon/SM +felonious +felony/SM +felt/MDGS +fem +female/PSM +femaleness/M +feminine/SMY +femininity/M +feminism/M +feminist/SM +feminize/DSG +femoral +femur/SM +fen/SM +fence/DRSMZG +fencer/M +fencing/M +fend/CDRZGS +fender/CM +fenestration/M +fennel/M +fentanyl/M +feral +ferment/FCMS +fermentation/M +fermented +fermenting +fermium/M +fern/MS +ferny/RT +ferocious/PY +ferociousness/M +ferocity/M +ferret/GSMD +ferric +ferromagnetic +ferromagnetism +ferrous +ferrule/MS +ferry/DSMG +ferryboat/SM +ferryman/M +ferrymen +fertile/I +fertility/IM +fertilization/M +fertilize/DRSZG +fertilized/U +fertilizer/M +ferule/SM +fervency/M +fervent/Y +fervid/Y +fervor/M +fess/FKGSD +fest/MRZVS +festal +fester/GMD +festival/SM +festive/YP +festiveness/M +festivity/SM +festoon/GMDS +feta/M +fetal +fetch/DRSZG +fetcher/M +fetching/Y +fete/MGDS +fetid/P +fetidness/M +fetish/MS +fetishism/M +fetishist/SM +fetishistic +fetlock/MS +fetter's +fetter/USGD +fettle/M +fettuccine/M +fetus/MS +feud/MDGS +feudal +feudalism/M +feudalistic +fever/SMD +feverish/YP +feverishness/M +few/TPMR +fewness/M +fey +fez/M +fezzes +ff +fiance/CM +fiancee/MS +fiances +fiasco/M +fiascoes +fiat/MS +fib/ZSMR +fibbed +fibber/SM +fibbing +fiber/M +fiberboard/M +fiberfill/M +fiberglass/M +fibril/SM +fibrillate/GNDS +fibrillation/M +fibrin/M +fibroid +fibrosis/M +fibrous +fibula/M +fibulae +fibular +fiche/SM +fichu/SM +fickle/RPT +fickleness/M +fiction/MS +fictional/Y +fictionalization/SM +fictionalize/DSG +fictitious/Y +fictive +ficus/M +fiddle/DRSMZG +fiddler/M +fiddlesticks +fiddly/TR +fidelity/IM +fidget/SGMD +fidgety +fiduciary/SM +fie +fief/MS +fiefdom/MS +field/ISMRZ +fielded +fielder/IM +fielding +fieldsman +fieldsmen +fieldwork/MRZ +fieldworker/M +fiend/SM +fiendish/Y +fierce/PRYT +fierceness/M +fieriness/M +fiery/RPT +fiesta/SM +fife/MZRS +fifer/M +fifteen/MHS +fifteenth/M +fifteenths +fifth/MY +fifths +fiftieth/M +fiftieths +fifty/SMH +fig/SLM +fight/SMRZG +fightback +fighter/IMS +fighting/IM +figment/MS +figuration/FM +figurative/Y +figure's +figure/FEGSD +figurehead/SM +figurine/MS +filament/MS +filamentous +filbert/MS +filch/DSG +file's/KC +file/CAKGDS +filename/S +filer/CSM +filet +filial +filibuster/MDRSZG +filibusterer/M +filigree/DSM +filigreeing +filing's +filings +fill's +fill/AIDGS +filled/U +filler/MS +fillet/MDGS +filling/SM +fillip/MDGS +filly/SM +film/MDGS +filminess/M +filmmaker/SM +filmstrip/MS +filmy/TPR +filo +filter/MDRBSZG +filtered/U +filterer/M +filth/M +filthily +filthiness/M +filthy/RPT +filtrate's +filtrate/IGNDS +filtration/IM +fin/SMR +finagle/DRSZG +finagler/M +final/SMY +finale/MS +finalist/SM +finality/M +finalization/M +finalize/DSG +finance's +finance/ADSG +financial/Y +financier/MS +financing/M +finch/MS +find/JMRZGS +finder/M +finding/M +findings/M +fine's/F +fine/CAFTGDS +finely +fineness/M +finery/AM +finespun +finesse/DSMG +finger/MDGSJ +fingerboard/SM +fingering/M +fingerling/SM +fingermark/S +fingernail/SM +fingerprint/SGMD +fingertip/MS +finial/MS +finical +finickiness/M +finicky/RPT +finis/MS +finish's +finish/ADSG +finished/U +finisher/MS +finite/IY +fink/MDGS +finned +finny +fir/ZGSJMDRH +fire/MS +firearm/SM +fireball/MS +firebomb/MDSJG +firebox/MS +firebrand/SM +firebreak/SM +firebrick/SM +firebug/SM +firecracker/SM +firedamp/M +firefight/MRSZG +firefighter/M +firefighting/M +firefly/SM +fireguard/S +firehouse/SM +firelight/ZMR +fireman/M +firemen +fireplace/SM +fireplug/MS +firepower/M +fireproof/DSG +firer/M +firescreen/S +fireside/MS +firestorm/MS +firetrap/MS +firetruck/MS +firewall/MS +firewater/M +firewood/M +firework/SM +firm/MDRYPTGS +firmament/SM +firmness/M +firmware/M +first/SMY +firstborn/SM +firsthand +firth/M +firths +fiscal/MYS +fish/MDRSZG +fishbowl/SM +fishcake/SM +fisher/M +fisherman/M +fishermen +fishery/SM +fishhook/SM +fishily +fishiness/M +fishing/M +fishmonger/MS +fishnet/SM +fishpond/MS +fishtail/DGS +fishwife/M +fishwives +fishy/TRP +fissile +fission/BM +fissure/SM +fist/MS +fistfight/MS +fistful/SM +fisticuffs/M +fistula/SM +fistulous/M +fit/KAMS +fitful/YP +fitfulness/M +fitly +fitment/S +fitness/UM +fitted/UA +fitter/MS +fittest +fitting/SMY +five/MZRS +fix/ZGBJMDRS +fixate/GNVDSX +fixation/M +fixative/MS +fixed/Y +fixer/M +fixings/M +fixity/M +fixture/MS +fizz/MDSG +fizzle/DSMG +fizzy/RT +fjord/SM +fl/JDG +flab/M +flabbergast/SGD +flabbily +flabbiness/M +flabby/RPT +flaccid/Y +flaccidity/M +flack/SM +flag/MS +flagella +flagellant/S +flagellate/GNDS +flagellation/M +flagellum/M +flagged +flagging/U +flagman/M +flagmen +flagon/MS +flagpole/SM +flagrance/M +flagrancy/M +flagrant/Y +flagship/SM +flagstaff/MS +flagstone/MS +flail/SGMD +flair/SM +flak/M +flake/DSMG +flakiness/M +flaky/TRP +flamage +flambe/MS +flambeed +flambeing +flamboyance/M +flamboyancy/M +flamboyant/Y +flame/DRSJMZG +flamenco/MS +flameproof/DGS +flamethrower/SM +flamingo/MS +flammability/IM +flammable/SM +flan/MS +flange/MS +flank/SZGMDR +flanker/M +flannel/SGMD +flannelette/M +flap/MS +flapjack/MS +flapped +flapper/SM +flapping +flare/DSMG +flareup/SM +flash/ZTGMDRS +flashback/SM +flashbulb/SM +flashcard/SM +flashcube/SM +flasher/M +flashgun/SM +flashily +flashiness/M +flashing/M +flashlight/MS +flashy/RTP +flask/SM +flat/MYPS +flatbed/SM +flatboat/SM +flatbread +flatcar/SM +flatfeet +flatfish/MS +flatfoot/SMD +flatiron/SM +flatland/M +flatlet/S +flatmate/S +flatness/M +flatted +flatten/SDG +flatter/SDRZG +flatterer/M +flattering/Y +flattery/M +flattest +flatting +flattish +flattop/SM +flatulence/M +flatulent +flatus/M +flatware/M +flatworm/SM +flaunt/MDSG +flaunting/Y +flavor/MDSGJ +flavored/U +flavorful +flavoring/M +flavorless +flavorsome +flaw/MDGS +flawless/PY +flawlessness/M +flax/MN +flay/DGS +flea/MS +fleabag/SM +fleabite/S +fleapit/S +fleck/SGMD +fledged/U +fledgling/MS +flee/S +fleece/MZGDRS +fleecer/M +fleeciness/M +fleecy/RTP +fleeing +fleet/STGMDRYP +fleetingly/M +fleetingness/M +fleetness/M +flesh/GMDSY +fleshly/TR +fleshpot/MS +fleshy/RT +flew +flex/AMS +flexed +flexibility/IM +flexible/I +flexibly/I +flexing +flexion +flextime/M +flibbertigibbet/SM +flick/SZGMDR +flicker/GMD +flier/M +flight/MS +flightiness/M +flightless +flighty/PTR +flimflam/SM +flimflammed +flimflamming +flimsily +flimsiness/M +flimsy/TRP +flinch/GMDS +fling/GM +flint/SM +flintlock/SM +flinty/TR +flip/MS +flippancy/M +flippant/Y +flipped +flipper/MS +flippest +flipping +flippy/S +flirt/SGMD +flirtation/MS +flirtatious/YP +flirtatiousness/M +flirty +flit/MS +flitted +flitting +float/SMDRZG +floater/M +flock/SMDG +flocking/M +floe/MS +flog/S +flogged +flogger/SM +flogging/MS +flood/SMDRG +floodgate/MS +floodlight/MDSG +floodlit +floodplain/MS +floodwater/MS +floor/SMDG +floorboard/MS +flooring/M +floorwalker/SM +floozy/SM +flop/MS +flophouse/MS +flopped +floppily +floppiness/M +flopping +floppy/PRSMT +flora/SM +floral +florescence/IM +florescent/I +floret/SM +florid/PY +floridness/M +florin/SM +florist/SM +floss/MDSG +flossy/RT +flotation/SM +flotilla/MS +flotsam/M +flounce/DSMG +flouncy +flounder/MDSG +flour/SMDG +flourish/GMDS +floury +flout/SMDRZG +flouter/M +flow/MDGS +flowchart/SM +flower's +flower/CSDG +flowerbed/MS +floweriness/M +flowering/S +flowerless +flowerpot/MS +flowery/PTR +flown +flt +flu/M +flub/MS +flubbed +flubbing +fluctuate/GNDSX +fluctuation/M +flue/MS +fluency/M +fluent/Y +fluff/SMDG +fluffiness/M +fluffy/RPT +fluid/SMY +fluidity/M +fluke/SM +fluky/RT +flume/SM +flummox/DSG +flung +flunk/SMDG +flunky/SM +fluoresce/DSG +fluorescence/M +fluorescent +fluoridate/GNDS +fluoridation/M +fluoride/SM +fluorine/M +fluorite/M +fluorocarbon/MS +fluoroscope/SM +fluoroscopic +fluoxetine +flurry/GDSM +flush/MDRSTG +fluster/MDSG +flute/DSMG +fluting/M +flutist/MS +flutter/MDSG +fluttery +fluvial +flux/IMS +fluxed +fluxing +fly/ZTGBDRSM +flyaway +flyblown +flyby/M +flybys +flycatcher/MS +flying/M +flyleaf/M +flyleaves +flyover/MS +flypaper/SM +flypast/S +flysheet/S +flyspeck/GMDS +flyswatter/MS +flytrap/S +flyway/SM +flyweight/SM +flywheel/MS +foal/MDGS +foam/MDGS +foaminess/M +foamy/RTP +fob/SM +fobbed +fobbing +focal/Y +focus's +focus/ADSG +focused/U +fodder/SM +foe/SM +fog's +fog/CS +fogbound +fogged/C +foggily +fogginess/M +fogging/C +foggy/RTP +foghorn/MS +fogy/SM +fogyish +foible/SM +foil/MDGS +foist/SDG +fol +fold's +fold/AUSGD +foldaway +folder/SM +foldout/MS +foliage/M +folic +folio/SM +folk/MS +folklore/M +folkloric +folklorist/MS +folksiness/M +folksinger/SM +folksinging/M +folksy/PTR +folktale/MS +folkway/MS +foll +follicle/MS +follow/SDRZGJ +follower/M +following/M +followup/S +folly/SM +foment/SGD +fomentation/M +fond/RYTP +fondant/MS +fondle/DSG +fondness/M +fondue/SM +font/MS +fontanel/MS +foo +foobar +food/MS +foodie/SM +foodstuff/SM +fool/MDGS +foolery/SM +foolhardily +foolhardiness/M +foolhardy/TPR +foolish/YP +foolishness/M +foolproof +foolscap/M +foot/MDRZGSJ +footage/M +football/MRZGS +footballer/M +footbridge/SM +footfall/MS +foothill/MS +foothold/MS +footie +footing/M +footless +footlights/M +footling/MS +footlocker/SM +footloose +footman/M +footmen +footnote/MGDS +footpath/M +footpaths +footplate/S +footprint/SM +footrace/MS +footrest/MS +footsie/SM +footslogging +footsore +footstep/MS +footstool/SM +footwear/M +footwork/M +footy +fop/SM +foppery/M +foppish/P +foppishness/M +for/H +fora +forage/DRSMZG +forager/M +foray/SMDG +forbade +forbear/SMG +forbearance/M +forbid/S +forbidden +forbidding/YS +forbore +forborne +force/DSMG +forced/U +forceful/PY +forcefulness/M +forceps/M +forcible +forcibly +ford/MDGSB +fore/MS +forearm/GSMD +forebear/MS +forebode/GJDS +foreboding/M +forecast/MRZGS +forecaster/M +forecastle/MS +foreclose/DSG +foreclosure/MS +forecourt/SM +foredoom/DGS +forefather/MS +forefeet +forefinger/SM +forefoot/M +forefront/SM +forego/G +foregoes +foregone +foreground/GMDS +forehand/MS +forehead/MS +foreign/ZRP +foreigner/M +foreignness/M +foreknew +foreknow/GS +foreknowledge/M +foreknown +foreleg/SM +forelimb/MS +forelock/MS +foreman/M +foremast/MS +foremen +foremost +forename/MDS +forenoon/MS +forensic/MS +forensically +forensics/M +foreordain/GSD +forepart/MS +foreperson/SM +foreplay/M +forequarter/MS +forerunner/MS +foresail/MS +foresaw +foresee/RSBZ +foreseeable/U +foreseeing +foreseen/U +foreseer/M +foreshadow/GDS +foreshore/S +foreshorten/DSG +foresight/MD +foresightedness/M +foreskin/MS +forest's +forest/ACGDS +forestall/SGD +forestation/ACM +forester/MS +forestland/M +forestry/M +foretaste/DSMG +foretell/GS +forethought/M +foretold +forever/M +forevermore +forewarn/DSG +forewent +forewoman/M +forewomen +foreword/MS +forfeit/GSMD +forfeiture/SM +forgather/SDG +forgave +forge/DRSMZGVJ +forger/M +forgery/SM +forget/S +forgetful/YP +forgetfulness/M +forgettable/U +forgetting +forging/M +forgivable/U +forgive/BRSZGP +forgiven +forgiveness/M +forgiver/M +forgiving/U +forgo/RZG +forgoer/M +forgoes +forgone +forgot +forgotten/U +fork/MDGS +forkful/SM +forklift/MS +forlorn/Y +form's +form/CAIFDGS +formal/SMY +formaldehyde/M +formalin +formalism/M +formalist/MS +formalities +formality/IM +formalization/M +formalize/GDS +format/SMV +formation/CFASM +formatted/A +formatting/M +formed/U +former/FIAM +formerly +formfitting +formic +formidable +formidably +formless/PY +formlessness/M +formula/MS +formulae +formulaic +formulate/ADSGNX +formulated/U +formulation/AM +formulator/SM +fornicate/GNDS +fornication/M +fornicator/MS +forsake/GS +forsaken +forsook +forsooth +forswear/SG +forswore +forsworn +forsythia/SM +fort/MS +forte/SM +forthcoming/M +forthright/YP +forthrightness/M +forthwith +fortieth/M +fortieths +fortification/M +fortified/U +fortifier/M +fortify/DRSNZGX +fortissimo +fortitude/M +fortnight/MYS +fortress/MS +fortuitous/YP +fortuitousness/M +fortuity/M +fortunate/UY +fortune/MS +fortuneteller/SM +fortunetelling/M +forty/SMH +forum/SM +forward/MDRYZTGSP +forwarder/M +forwardness/M +forwent +fossa +fossil/SM +fossilization/M +fossilize/GDS +foster/GSD +fought +foul/MDRYTGSP +foulard/M +foulmouthed +foulness/M +found/FSDG +foundation/SM +foundational +founded/U +founder/GMDS +foundling/SM +foundry/SM +fount/SM +fountain/SM +fountainhead/MS +four/MHS +fourfold +fourposter/SM +fourscore/M +foursome/SM +foursquare +fourteen/SMH +fourteenth/M +fourteenths +fourth/MY +fourths +fowl/MDGS +fox/GMDS +foxfire/M +foxglove/SM +foxhole/MS +foxhound/SM +foxhunt/GS +foxily +foxiness/M +foxtrot/MS +foxtrotted +foxtrotting +foxy/RTP +foyer/SM +fps +fr +fracas/MS +frack/SDG +fractal/SM +fraction/ISM +fractional/Y +fractious/YP +fractiousness/M +fracture/MGDS +frag/S +fragile/RT +fragility/M +fragment/GMDS +fragmentary/M +fragmentation/M +fragrance/MS +fragrant/Y +frail/RYTP +frailness/M +frailty/SM +frame/DRSMZG +framed/U +framer/M +framework/SM +franc/SM +franchise's +franchise/EDSG +franchisee/SM +franchiser/SM +francium/M +francophone +frangibility/M +frangible +frank/SMDRYTGP +frankfurter/MS +frankincense/M +frankness/M +frantic +frantically +frappe/SM +frat/MS +fraternal/Y +fraternity/FSM +fraternization/M +fraternize/ZGDRS +fraternizer/M +fratricidal +fratricide/MS +fraud's +fraud/S +fraudster/S +fraudulence/M +fraudulent/Y +fraught +fray's +fray/CDGS +frazzle/MGDS +freak/SMDG +freakish/YP +freakishness/M +freaky/RT +freckle/DSMG +freckly +free/YTDRS +freebase/MGDS +freebie/SM +freebooter/SM +freeborn +freedman/M +freedmen +freedom/SM +freehand +freehold/ZMRS +freeholder/M +freeing +freelance/DRSMZG +freelancer/M +freeload/SDRZG +freeloader/M +freeman/M +freemasonry +freemen +freephone +freesia/S +freestanding +freestone/SM +freestyle/SM +freethinker/SM +freethinking/M +freeware/M +freeway/MS +freewheel/DGS +freewill +freezable +freeze's +freeze/UAGS +freezer/MS +freezing's +freight/MDRZGS +freighter/M +french +frenemy/S +frenetic +frenetically +frenzied/Y +frenzy/DSM +freq +frequencies +frequency/IM +frequent/DRYSZTG +frequented/U +frequenter/M +fresco/M +frescoes +fresh/PNRYXZT +freshen/ZGDR +freshener/M +freshet/MS +freshman/M +freshmen +freshness/M +freshwater/M +fret/MS +fretful/YP +fretfulness/M +fretsaw/MS +fretted +fretting +fretwork/M +friable +friar/SM +friary/SM +fricassee/DSM +fricasseeing +fricative/SM +friction/SM +frictional +fridge/SM +friedcake/MS +friend's +friend/UGSDY +friendless +friendlies +friendliness/UM +friendly's +friendly/UPTR +friendship/MS +frieze/SM +frig/S +frigate/MS +frigged +frigging +fright/SXGMDN +frighten/DG +frightening/Y +frightful/PY +frightfulness/M +frigid/YP +frigidity/M +frigidness/M +frill/SMD +frilly/TR +fringe's +fringe/IDSG +frippery/SM +frisk/SDG +friskily +friskiness/M +frisky/TRP +frisson/S +fritter/MDSG +fritz/M +frivolity/SM +frivolous/PY +frivolousness/M +frizz/MDSYG +frizzle/MGDS +frizzy/TR +fro +frock's +frock/CUS +frog/MS +frogging/S +frogman/M +frogmarch/GDS +frogmen +frogspawn +frolic/SM +frolicked +frolicker/SM +frolicking +frolicsome +from +frond/SM +front's +front/FSDG +frontage/MS +frontal/Y +frontbench/ZRS +frontier/MS +frontiersman/M +frontiersmen +frontierswoman +frontierswomen +frontispiece/MS +frontward/S +frosh/M +frost's +frost/CSDG +frostbit +frostbite/MGS +frostbitten +frostily +frostiness/M +frosting/SM +frosty/TPR +froth/MDG +frothiness/M +froths +frothy/TPR +froufrou/M +frown/SMDG +frowzily +frowziness/M +frowzy/TPR +froze/AU +frozen/UA +fructify/DSG +fructose/M +frugal/Y +frugality/M +fruit/SMDG +fruitcake/MS +fruiterer/S +fruitful/YP +fruitfulness/M +fruitiness/M +fruition/M +fruitless/PY +fruitlessness/M +fruity/TPR +frump/SM +frumpish +frumpy/TR +frustrate/GNXDS +frustrating/Y +frustration/M +frustum/MS +fry/GDSM +fryer/SM +ft +ftp/ZGS +fuchsia/MS +fuck/SMGDRZ! +fucker/M! +fuckhead/S! +fuddle/DSMG +fudge/DSMG +fuehrer/MS +fuel's +fuel/ADGS +fug +fugal +fuggy +fugitive/MS +fugue/SM +fuhrer/SM +fulcrum/MS +fulfill/LDGS +fulfilled/U +fulfilling/U +fulfillment/M +full/MDRZTGSP +fullback/MS +fuller/M +fullness/M +fully +fulminate/DSXGN +fulmination/M +fulsome/PY +fulsomeness/M +fum/S +fumble/DRSMZG +fumbler/M +fumbling/Y +fume/MGDS +fumigant/MS +fumigate/GNDS +fumigation/M +fumigator/SM +fumy/RT +fun/M +function/MDGS +functional/Y +functionalism +functionalist/S +functionality/S +functionary/SM +functor +fund/AMDGS +fundamental/SMY +fundamentalism/M +fundamentalist/SM +funded/U +funding/M +fundraiser/MS +fundraising +funeral/MS +funerary +funereal/Y +funfair/S +fungal +fungi +fungible/MS +fungicidal +fungicide/MS +fungoid +fungous +fungus/M +funicular/SM +funk/MDGS +funkiness/M +funky/PRT +funnel/MDGS +funner +funnest +funnily +funniness/M +funny/TPRSM +funnyman/M +funnymen +fur/SM +furbelow/M +furbish/ADSG +furious/Y +furl's +furl/UDGS +furlong/SM +furlough/GMD +furloughs +furn +furnace/SM +furnish/ADSG +furnished/U +furnishings/M +furniture/M +furor/SM +furosemide +furred +furrier/M +furriness/M +furring/M +furrow/MDSG +furry/ZTRP +further/SGD +furtherance/M +furthermore +furthermost +furthest +furtive/YP +furtiveness/M +fury/SM +furze/M +fuse's/A +fuse/CAIFGDS +fusee/SM +fuselage/SM +fusibility/M +fusible +fusilier/SM +fusillade/MS +fusion/IFKSM +fuss/MDSG +fussbudget/MS +fussily +fussiness/M +fusspot/SM +fussy/TRP +fustian/M +fustiness/M +fusty/TRP +fut +futile/Y +futility/M +futon/SM +future/MS +futurism/M +futurist/MS +futuristic +futurity/SM +futurologist/MS +futurology/M +futz/DSG +fuzz/MDSG +fuzzball/S +fuzzily +fuzziness/M +fuzzy/PTR +fwd +fwy +g/SNXVB +gab/SM +gabardine/SM +gabbed +gabbiness/M +gabbing +gabble/DSMG +gabby/RTP +gaberdine/SM +gabfest/MS +gable/DSM +gad/S +gadabout/SM +gadded +gadder/SM +gadding +gadfly/SM +gadget/SM +gadgetry/M +gadolinium/M +gaff/MDRZGS +gaffe/SM +gaffer/M +gag/SM +gaga +gagged +gagging +gaggle/SM +gaiety/M +gaily +gain's +gain/ADGS +gainer/SM +gainful/Y +gainsaid +gainsay/ZGRS +gainsayer/M +gait/MRZS +gaiter/M +gal/SM +gala/MS +galactic +galaxy/SM +gale's +gale/AS +galena/M +gall/MDGS +gallant/SMY +gallantry/M +gallbladder/MS +galleon/SM +galleria/MS +gallery/SM +galley/SM +gallimaufry/SM +gallium/M +gallivant/GSD +gallon/SM +gallop/SMDG +gallows/M +gallstone/MS +galoot/SM +galore +galosh/MS +galumph/DG +galumphs +galvanic +galvanism/M +galvanization/M +galvanize/DSG +galvanometer/MS +gambit/SM +gamble/DRSMZG +gambler/M +gambling/M +gambol/SMDG +game/MYTGDRSP +gamecock/MS +gamekeeper/MS +gameness/M +gamesmanship/M +gamester/MS +gamete/SM +gametic +gamin/SM +gamine/SM +gaminess/M +gaming/M +gamma/SM +gammon/M +gammy +gamut/SM +gamy/RTP +gander/SM +gang/MDGS +gangbusters/M +gangland/M +ganglia +gangling +ganglion/M +ganglionic +gangplank/SM +gangrene/DSMG +gangrenous +gangsta/S +gangster/SM +gangway/MS +ganja +gannet/SM +gantlet/MS +gantry/SM +gap/GSMD +gape/MS +gar/SLM +garage/DSMG +garb/MDGS +garbage/M +garbageman +garbanzo/SM +garble/DSG +garcon/SM +garden/SZGMDR +gardener/M +gardenia/MS +gardening/M +garfish/MS +gargantuan +gargle/DSMG +gargoyle/SM +garish/PY +garishness/M +garland/MDGS +garlic/M +garlicky +garment/MS +garner/SGD +garnet/SM +garnish/GLMDS +garnishee/DSM +garnisheeing +garnishment/SM +garret/SM +garrison/MDSG +garrote/MZGDRS +garroter/M +garrulity/M +garrulous/PY +garrulousness/M +garter/SM +gas's +gas/CS +gasbag/SM +gaseous +gash/MDSG +gasholder/S +gasket/SM +gaslight/MS +gasman +gasmen +gasohol/M +gasoline/M +gasometer/S +gasp/MDGS +gassed/C +gasses +gassing/C +gassy/RT +gastric +gastritis/M +gastroenteritis/M +gastrointestinal +gastronome/S +gastronomic +gastronomical/Y +gastronomy/M +gastropod/SM +gasworks/M +gate/MGDS +gateau +gateaux +gatecrash/DRSZG +gatecrasher/M +gatehouse/SM +gatekeeper/MS +gatepost/MS +gateway/MS +gather/SJZGMDR +gatherer/M +gathering/M +gator/SM +gauche/RPYT +gaucheness/M +gaucherie/M +gaucho/SM +gaudily +gaudiness/M +gaudy/RPT +gauge/DSMG +gaunt/RPT +gauntlet/MS +gauntness/M +gauze/M +gauziness/M +gauzy/RPT +gave +gavel/SM +gavotte/MS +gawd +gawk/DGS +gawkily +gawkiness/M +gawky/RPT +gawp/DGS +gay/TSPMR +gayness/M +gaze/MZGDRS +gazebo/SM +gazelle/MS +gazer/M +gazette/MGDS +gazetteer/MS +gazillion/S +gazpacho/M +gazump/DGS +gear/MDGS +gearbox/MS +gearing/M +gearshift/MS +gearwheel/SM +gecko/SM +geddit +gee/DS +geeing +geek/MS +geeky/RT +geese +geezer/MS +geisha/M +gel/SM +gelatin/M +gelatinous +gelcap/M +geld/DJGS +gelding/M +gelid +gelignite/M +gelled +gelling +gem/SM +gemological +gemologist/MS +gemology/M +gemstone/MS +gendarme/MS +gender/MDS +gene/MS +genealogical/Y +genealogist/MS +genealogy/SM +genera +general/SMY +generalissimo/MS +generalist/MS +generality/SM +generalization/MS +generalize/GDS +generalship/M +generate/CAVNGSD +generation/ACM +generational +generations +generator/SM +generic/SM +generically +generosity/SM +generous/PY +generousness/M +genes/S +genesis/M +genetic/S +genetically +geneticist/MS +genetics/M +genial/FY +geniality/FM +geniculate +genie/SM +genii +genital/FY +genitalia/M +genitals/M +genitive/MS +genitourinary +genius/MS +genned +genning +genocidal +genocide/MS +genome/MS +genomics +genre/SM +gent/AMS +genteel/YP +genteelness/M +gentian/SM +gentile/SM +gentility/M +gentle/TGDRSP +gentlefolk/MS +gentlefolks/M +gentleman/MY +gentlemanly/U +gentlemen +gentleness/M +gentlewoman/M +gentlewomen +gently +gentrification/M +gentrify/DSGN +gentry/SM +genuflect/DGS +genuflection/MS +genuine/PY +genuineness/M +genus/M +geocache/DSG +geocentric +geocentrically +geochemistry/M +geode/SM +geodesic/SM +geodesy/M +geodetic +geoengineering +geog +geographer/SM +geographic +geographical/Y +geography/SM +geologic +geological/Y +geologist/MS +geology/SM +geom +geomagnetic +geomagnetism/M +geometer +geometric +geometrical/Y +geometry/SM +geophysical +geophysicist/SM +geophysics/M +geopolitical +geopolitics/M +geostationary +geosynchronous +geosyncline/MS +geothermal +geothermic +geranium/MS +gerbil/MS +geriatric/S +geriatrician/S +geriatrics/M +germ/MS +germane +germanium/M +germicidal +germicide/MS +germinal/M +germinate/GNDS +germination/M +gerontological +gerontologist/MS +gerontology/M +gerrymander/GMDS +gerrymandering/M +gerund/MS +gestalt/S +gestapo/MS +gestate/GNDS +gestation/M +gestational +gesticulate/DSGNX +gesticulation/M +gestural +gesture/MGDS +gesundheit +get/S +getaway/SM +getting +getup/M +gewgaw/SM +geyser/SM +ghastliness/M +ghastly/TPR +ghat/MS +ghee +gherkin/MS +ghetto/SM +ghettoize/GDS +ghost/SMDYG +ghostliness/M +ghostly/RTP +ghostwrite/ZGRS +ghostwriter/M +ghostwritten +ghostwrote +ghoul/SM +ghoulish/YP +ghoulishness/M +giant/SM +giantess/MS +gibber/GDS +gibberish/M +gibbet/GMDS +gibbon/MS +gibbous +gibe/MGDS +giblet/SM +giddily +giddiness/M +giddy/RTP +gift/MDGS +gig/SM +gigabit/SM +gigabyte/MS +gigagram/S +gigahertz/M +gigajoule/S +gigameter/S +gigantic +gigantically +gigapascal/S +gigapixel/MS +gigawatt/SM +gigged +gigging +giggle/DRSMZG +giggler/M +giggly/RT +gigolo/SM +gild/MDRZGS +gilder/M +gilding/M +gill/MS +gillie/S +gillion/S +gilt/MS +gimbals/M +gimcrack/SM +gimcrackery/M +gimlet/GSMD +gimme/SM +gimmick/MS +gimmickry/M +gimmicky +gimp/MDGS +gimpy +gin/SM +ginger/GSMDY +gingerbread/M +gingersnap/SM +gingery +gingham/M +gingivitis/M +ginkgo/M +ginkgoes +ginned +ginning +ginormous +ginseng/M +giraffe/MS +gird/DRZGS +girder/M +girdle/DSMG +girl/MS +girlfriend/MS +girlhood/SM +girlish/YP +girlishness/M +girly +giro/S +girt/MDGS +girth/M +girths +gist/M +git/S +gite/S +give/ZGJRS +giveaway/MS +giveback/MS +given/SM +giver/M +gizmo/SM +gizzard/MS +glace/S +glaceed +glaceing +glacial/Y +glaciate/XGNDS +glaciation/M +glacier/MS +glad/MYSP +gladden/GDS +gladder +gladdest +glade/SM +gladiator/SM +gladiatorial +gladiola/SM +gladioli +gladiolus/M +gladness/M +gladsome +glam +glamorization/M +glamorize/DSG +glamorous/Y +glamour/GMDS +glance/DSMG +gland/SM +glandes +glandular +glans/M +glare/DSMG +glaring/Y +glasnost/M +glass/MDSG +glassblower/MS +glassblowing/M +glassful/SM +glasshouse/S +glassily +glassiness/M +glassware/M +glassy/RTP +glaucoma/M +glaze/DSMG +glazier/SM +glazing/M +gleam/SMDGJ +glean/SDRZGJ +gleaner/M +gleanings/M +glee/M +gleeful/YP +gleefulness/M +glen/MS +glenohumeral +glenoid +glib/YP +glibber +glibbest +glibness/M +glide/DRSMZG +glider/M +gliding/M +glimmer/MDGJS +glimmering/M +glimpse/MGDS +glint/SMDG +glissandi +glissando/M +glisten/MDSG +glister/DSG +glitch/GMDS +glitter/MDSG +glitterati +glittery +glitz/M +glitzy/TR +gloaming/SM +gloat/SMDG +gloating/Y +glob/MDGS +global/Y +globalism/M +globalist/MS +globalization/M +globalize/GDS +globe/SM +globetrotter/MS +globetrotting +globular +globule/MS +globulin/M +glockenspiel/SM +gloom/M +gloomily +gloominess/M +gloomy/TRP +glop/M +gloppy +glorification/M +glorify/GDSN +glorious/IY +glory/DSMG +gloss/MDSG +glossary/SM +glossily +glossiness/M +glossolalia/M +glossy/PTRSM +glottal +glottis/MS +glove/DSMG +glow/MDRZGS +glower/GMD +glowing/Y +glowworm/MS +glucagon +glucose/M +glue/MGDS +glued/U +gluey +gluier +gluiest +glum/YP +glummer +glummest +glumness/M +gluon/S +glut/MNS +gluten/M +glutenous +glutinous/Y +glutted +glutting +glutton/MS +gluttonous/Y +gluttony/M +glycerin/M +glycerol/M +glycogen/M +glycol +glyph +gm +gnarl/SMDG +gnarly/TR +gnash/MDSG +gnat/MS +gnaw/DGS +gneiss/M +gnocchi +gnome/SM +gnomic +gnomish +gnu/SM +go/JMRHZG +goad/MDGS +goal/MS +goalie/SM +goalkeeper/MS +goalkeeping/M +goalless +goalmouth +goalmouths +goalpost/MS +goalscorer/S +goaltender/MS +goat/MS +goatee/SM +goatherd/MS +goatskin/MS +gob/SM +gobbed +gobbet/SM +gobbing +gobble/DRSMZG +gobbledygook/M +gobbler/M +goblet/SM +goblin/SM +gobsmacked +gobstopper/S +god/SM +godawful +godchild/M +godchildren/M +goddammit +goddamn/D +goddaughter/MS +goddess/MS +godfather/SM +godforsaken +godhead/M +godhood/M +godless/PY +godlessness/M +godlike +godliness/UM +godly/URTP +godmother/SM +godparent/SM +godsend/SM +godson/SM +godspeed +goer/M +goes +gofer/SM +goggle/DSMG +goggles/M +going/M +goiter/SM +gold/MNS +goldbrick/ZGSMDR +goldbricker/M +golden/TR +goldenrod/M +goldfield/S +goldfinch/MS +goldfish/MS +goldmine/SM +goldsmith/M +goldsmiths +golf/MDRZGS +golfer/M +golliwog/S +golly/SM +gonad/SM +gonadal +gondola/MS +gondolier/SM +gone/ZR +goner/M +gong/MDGS +gonk/S +gonna +gonorrhea/M +gonorrheal +gonzo +goo/M +goober/SM +good/MYSP +goodbye/MS +goodhearted +goodish +goodly/TR +goodness/M +goodnight +goods/M +goodwill/M +goody/SM +gooey +goof/MDGS +goofball/SM +goofiness/M +goofy/RPT +google/DSMG +googly/S +gooier +gooiest +gook/MS +goon/MS +goop/M +goose/DSMG +gooseberry/SM +goosebumps/M +goosestep/S +goosestepped +goosestepping +gopher/SM +gore/MGDS +gorge's +gorge/EDSG +gorgeous/YP +gorgeousness/M +gorgon/SM +gorilla/MS +gorily +goriness/M +gormandize/DRSZG +gormandizer/M +gormless +gorp/MS +gorse/M +gory/RTP +gosh +goshawk/MS +gosling/SM +gospel/MS +gossamer/M +gossip/MDRZGS +gossiper/M +gossipy +got +gotcha/S +goths +gotta +gotten +gouache/S +gouge/DRSMZG +gouger/M +goulash/MS +gourd/SM +gourde/MS +gourmand/SM +gourmet/SM +gout/M +gouty/TR +gov +govern/DGSBL +governable/U +governance/M +governed/U +governess/MS +government/MS +governmental +governor/SM +governorship/M +govt +gown/MDGS +gr +grab/MS +grabbed +grabber/MS +grabbing +grabby/TR +grace/EDSMG +graceful/EPY +gracefulness/EM +graceless/PY +gracelessness/M +gracious/UY +graciousness/M +grackle/MS +grad/MRZSB +gradate/XGNDS +gradation/CM +grade's +grade/CADSG +graded/U +grader/M +gradient/MS +gradual/PY +gradualism/M +gradualness/M +graduate/XMGNDS +graduation/M +graffiti +graffito/M +graft/SMDRZG +grafter/M +graham/S +grail +grain/ISMD +graininess/M +grainy/PTR +gram/KMS +grammar/MS +grammarian/SM +grammatical/UY +gramophone/MS +grampus/MS +gran/S +granary/SM +grand/SMRYPT +grandam/MS +grandaunt/MS +grandchild/M +grandchildren/M +granddad/SM +granddaddy/SM +granddaughter/SM +grandee/MS +grandeur/M +grandfather/GMDYS +grandiloquence/M +grandiloquent +grandiose/Y +grandiosity/M +grandma/MS +grandmother/MYS +grandnephew/MS +grandness/M +grandniece/MS +grandpa/MS +grandparent/MS +grandson/MS +grandstand/SGMD +granduncle/SM +grange/SM +granite/M +granitic +granny/SM +granola/M +grant/SMDRZG +grantee/MS +granter/M +grantsmanship/M +granular +granularity/M +granulate/GNDS +granulation/M +granule/MS +grape/SM +grapefruit/MS +grapeshot/M +grapevine/SM +graph/MDG +graphic/MS +graphical/Y +graphite/M +graphologist/MS +graphology/M +graphs +grapnel/MS +grapple/MGDS +grasp/SMDBG +grass/MDSG +grasshopper/MS +grassland/MS +grassroots +grassy/TR +grate/DRSMZGJ +grateful/UYP +gratefulness/UM +grater/M +gratification/M +gratify/GNXDS +gratifying/Y +gratin/S +grating/MY +gratis +gratitude/IM +gratuitous/YP +gratuitousness/M +gratuity/SM +gravamen/MS +grave/DRSMYTGP +gravedigger/SM +gravel/SGMDY +graven +graveness/M +graveside/MS +gravestone/SM +graveyard/MS +gravid +gravimeter/MS +gravitas +gravitate/GNDS +gravitation/M +gravitational +gravity/M +gravy/SM +gray/MDRTGSP +graybeard/SM +grayish +grayness/M +graze/DRSMZG +grazer/M +grease/DRSMZG +greasepaint/M +greasily +greasiness/M +greasy/PTR +great/SMRYPT +greatcoat/SM +greathearted +greatness/M +grebe/SM +greed/M +greedily +greediness/M +greedy/PTR +green/GPSMDRYT +greenback/MS +greenbelt/MS +greenery/M +greenfield +greenfly/S +greengage/MS +greengrocer/SM +greenhorn/SM +greenhouse/SM +greenish +greenmail/M +greenness/M +greenroom/SM +greenstone +greensward/M +greenwood/M +greet/ZGJSDR +greeter/M +greeting/M +gregarious/PY +gregariousness/M +gremlin/SM +grenade/SM +grenadier/MS +grenadine/M +grep/S +grepped +grepping +grew/A +greyhound/SM +gribble/S +grid/MS +griddle/SM +griddlecake/SM +gridiron/SM +gridlock/SMD +grief/SM +grievance/MS +grieve/ZGDRS +griever/M +grievous/PY +grievousness/M +griffin/SM +griffon/SM +grill/SGMDJ +grille/MS +grim/DYPG +grimace/DSMG +grime/SM +griminess/M +grimmer +grimmest +grimness/M +grimy/TRP +grin/MS +grind/SZGMRJ +grinder/M +grindstone/MS +gringo/MS +grinned +grinning +grip/MDRSZG +gripe/SM +griper/M +grippe/MZGDR +gripper/M +grisliness/M +grisly/RTP +grist/MY +gristle/M +gristmill/MS +grit/MS +grits/M +gritted +gritter/SM +grittiness/M +gritting +gritty/RTP +grizzle/DSG +grizzly/TRSM +groan/SGMD +groat/SM +grocer/MS +grocery/SM +grog/M +groggily +grogginess/M +groggy/PRT +groin/SM +grok/S +grokked +grokking +grommet/SM +groom/SZGMDR +groomer/M +grooming/M +groomsman/M +groomsmen +groove/MGDS +groovy/RT +grope/DRSMZG +groper/M +grosbeak/MS +grosgrain/M +gross/PTGMDRSY +grossness/M +grotesque/SPMY +grotesqueness/M +grotto/M +grottoes +grotty/TR +grouch/GMDS +grouchily +grouchiness/M +grouchy/RTP +ground/ZGMDRJS +groundbreaking/MS +groundcloth +groundcloths +grounder/M +groundhog/MS +grounding/M +groundless/Y +groundnut/MS +groundsheet/S +groundskeeper/S +groundsman +groundsmen +groundswell/SM +groundwater/M +groundwork/M +group/JSZGMDR +grouper/M +groupie/MS +grouping/M +groupware/M +grouse/MZGDRS +grouser/M +grout/SGMD +grove/SM +grovel/ZGDRS +groveler/M +grovelled +grovelling +grow/AHSG +grower/MS +growing/I +growl/SZGMDR +growler/M +grown/AI +grownup/MS +growth/AM +growths +grub/MS +grubbed +grubber/MS +grubbily +grubbiness/M +grubbing +grubby/TRP +grubstake/M +grudge/MGDS +grudging/Y +grue/S +gruel/GJM +grueling/Y +gruesome/RYTP +gruesomeness/M +gruff/TPRY +gruffness/M +grumble/DRSMZGJ +grumbler/M +grump/SM +grumpily +grumpiness/M +grumpy/PRT +grunge/MS +grungy/RT +grunion/SM +grunt/SGMD +gt +guacamole/M +guanine/M +guano/M +guarani/MS +guarantee/MDS +guaranteeing +guarantor/MS +guaranty/GDSM +guard/SZGMDR +guarded/Y +guarder/M +guardhouse/SM +guardian/SM +guardianship/M +guardrail/SM +guardroom/SM +guardsman/M +guardsmen +guava/SM +gubernatorial +guerrilla/SM +guess/ZGBMDRS +guesser/M +guesstimate/DSMG +guesswork/M +guest/SGMD +guestbook/SM +guesthouse/S +guestroom/S +guff/M +guffaw/MDGS +guidance/M +guide/DRSMZG +guidebook/SM +guided/U +guideline/SM +guidepost/SM +guider/M +guild/SZMR +guilder/M +guildhall/MS +guile/M +guileful +guileless/YP +guilelessness/M +guillemot/S +guillotine/DSMG +guilt/M +guiltily +guiltiness/M +guiltless +guilty/PRT +guinea/MS +guise/ESM +guitar/MS +guitarist/SM +gulag/SM +gulch/MS +gulden/MS +gulf/MS +gull/MDSG +gullet/MS +gullibility/M +gullible +gully/SM +gulp/MDRSZG +gulper/M +gum/SM +gumball/S +gumbo/SM +gumboil/SM +gumboot/S +gumdrop/SM +gummed +gumming +gummy/TR +gumption/M +gumshoe/MDS +gumshoeing +gun/SM +gunboat/SM +gunfight/MRZS +gunfighter/M +gunfire/M +gunge +gungy +gunk/M +gunky +gunman/M +gunmen +gunmetal/M +gunned +gunnel/MS +gunner/MS +gunnery/M +gunning +gunny/M +gunnysack/MS +gunpoint/M +gunpowder/M +gunrunner/MS +gunrunning/M +gunship/MS +gunshot/MS +gunslinger/SM +gunsmith/M +gunsmiths +gunwale/MS +guppy/SM +gurgle/MGDS +gurney/MS +guru/MS +gush/MDRSZG +gusher/M +gushing/Y +gushy/TR +gusset/MSDG +gussy/DSG +gust/EMDSG +gustatory +gustily +gusto/M +gusty/RT +gut/SM +gutless/P +gutlessness/M +gutsy/RT +gutted +gutter/SMDG +guttersnipe/MS +gutting +guttural/MS +gutty/RT +guv/S +guvnor/S +guy/SGMD +guzzle/DRSZG +guzzler/M +gym/SM +gymkhana/MS +gymnasium/MS +gymnast/MS +gymnastic/S +gymnastically +gymnastics/M +gymnosperm/SM +gymslip/S +gynecologic +gynecological +gynecologist/SM +gynecology/M +gyp/SM +gypped +gypper/SM +gypping +gypster/SM +gypsum/M +gypsy/SM +gyrate/DSGNX +gyration/M +gyrator/SM +gyrfalcon/MS +gyro/MS +gyroscope/MS +gyroscopic +gyve/MGDS +h'm +h/NRSXZGVJ +ha/SH +haberdasher/SM +haberdashery/SM +habiliment/SM +habit's +habit/ISB +habitability/M +habitat/SM +habitation/MS +habitual/YP +habitualness/M +habituate/GNDS +habituation/M +habitue/SM +hacienda/SM +hack/MDRZGS +hacker/M +hacking/M +hackish +hackle/MS +hackney/SMDG +hacksaw/SM +hacktivist/MS +hackwork/M +had +haddock/SM +hadith +hadn't +hadst +hafnium/M +haft/MS +hag/SM +haggard/YP +haggardness/M +haggis/MS +haggish +haggle/MZGDRS +haggler/M +hagiographer/SM +hagiography/SM +hahnium/M +haiku/M +hail/MDGS +hailstone/MS +hailstorm/MS +hair/MDS +hairball/MS +hairband/S +hairbreadth/M +hairbreadths +hairbrush/MS +haircloth/M +haircut/SM +hairdo/MS +hairdresser/SM +hairdressing/M +hairdryer/MS +hairgrip/S +hairiness/M +hairless +hairlike +hairline/SM +hairnet/SM +hairpiece/MS +hairpin/SM +hairsbreadth/M +hairsbreadths +hairsplitter/SM +hairsplitting/M +hairspray/S +hairspring/MS +hairstyle/MS +hairstylist/SM +hairy/TRP +haj +hajj/M +hajjes +hajji/SM +hake/MS +halal/M +halberd/SM +halcyon +hale/ITGDRS +half/M +halfback/SM +halfhearted/PY +halfheartedness/M +halfpence +halfpenny/SM +halftime/MS +halftone/MS +halfway +halfwit/SM +halibut/SM +halite/M +halitosis/M +hall/MS +hallelujah/M +hallelujahs +hallmark/GMDS +halloo/MSG +hallow/DSG +hallowed/U +hallucinate/GNXDS +hallucination/M +hallucinatory +hallucinogen/SM +hallucinogenic/SM +hallway/SM +halo/MDGS +halogen/SM +halon +halt/MDRZGS +halter/GMD +halterneck/S +halting/Y +halve/DSG +halyard/MS +ham/SM +hamburg/SZMR +hamburger/M +hamlet/MS +hammed +hammer/MDRSJZG +hammerer/M +hammerhead/SM +hammerlock/SM +hammertoe/MS +hamming +hammock/SM +hammy/TR +hamper/GMDS +hampered/U +hamster/MS +hamstring/GSM +hamstrung +hand's +hand/UDGS +handbag/SM +handball/MS +handbarrow/SM +handbill/MS +handbook/MS +handbrake/S +handcar/SM +handcart/MS +handclasp/MS +handcraft/SMDG +handcuff/MDGS +handed/P +handful/SM +handgun/SM +handheld/MS +handhold/MS +handicap/MS +handicapped +handicapper/MS +handicapping +handicraft/MS +handily +handiness/M +handiwork/M +handkerchief/MS +handle/MZGDRS +handlebar/MS +handler/M +handmade +handmaid/XMNS +handmaiden/M +handout/SM +handover/S +handpick/GDS +handrail/MS +handsaw/SM +handset/SM +handshake/JMGS +handsome/PYTR +handsomeness/M +handspring/MS +handstand/SM +handwork/M +handwoven +handwriting/M +handwritten +handy/UTR +handyman/M +handymen +hang/MDRJZGS +hangar/MS +hangdog +hanger/M +hanging/M +hangman/M +hangmen +hangnail/MS +hangout/SM +hangover/MS +hangup/MS +hank/MRZS +hanker/GJD +hankering/M +hankie/MS +hansom/MS +hap/MY +haphazard/YP +haphazardness/M +hapless/YP +haplessness/M +haploid/MS +happen/SDGJ +happening/M +happenstance/SM +happily/U +happiness/UM +happy/URTP +haptic +harangue/MGDS +harass/LZGDRS +harasser/M +harassment/M +harbinger/SM +harbor/GMDS +harbormaster/S +hard/NRYXTP +hardback/MS +hardball/M +hardboard/M +hardbound +hardcore +hardcover/SM +harden/ZGDR +hardened/U +hardener/M +hardhat/MS +hardheaded/PY +hardheadedness/M +hardhearted/PY +hardheartedness/M +hardihood/M +hardily +hardiness/M +hardliner/MS +hardness/M +hardscrabble +hardship/SM +hardstand/SM +hardtack/M +hardtop/SM +hardware/M +hardwired +hardwood/SM +hardworking +hardy/PTR +hare/MGDS +harebell/MS +harebrained +harelip/SM +harelipped +harem/SM +haricot/S +hark/DGS +harlequin/SM +harlot/SM +harlotry/M +harm/MDGS +harmed/U +harmful/YP +harmfulness/M +harmless/PY +harmlessness/M +harmonic/SM +harmonica/MS +harmonically +harmonies +harmonious/PY +harmoniousness/M +harmonium/MS +harmonization/M +harmonize/ZGDRS +harmonizer/M +harmony/EM +harness's +harness/UDSG +harp/MDGS +harpist/SM +harpoon/ZGSMDR +harpooner/M +harpsichord/MS +harpsichordist/SM +harpy/SM +harridan/MS +harrier/M +harrow/SMDG +harrumph/GD +harrumphs +harry/DRSZG +harsh/RYTP +harshness/M +hart/MS +harvest/SMDRZG +harvested/U +harvester/M +hash/AMDSG +hashish/M +hashtag/SM +hasn't +hasp/MS +hassle/DSMG +hassock/SM +hast/DNXG +haste/SM +hasten/DG +hastily +hastiness/M +hasty/RTP +hat/ZGSMDR +hatband/S +hatbox/MS +hatch/MDSG +hatchback/MS +hatcheck/SM +hatched/U +hatchery/SM +hatchet/SM +hatching/M +hatchway/SM +hate/MS +hateful/PY +hatefulness/M +hatemonger/MS +hater/M +hatpin/S +hatred/SM +hatstand/S +hatted +hatter/SM +hatting +hauberk/SM +haughtily +haughtiness/M +haughty/PRT +haul/MDRZGS +haulage/M +hauler/M +haulier/S +haunch/MS +haunt/SMDRZG +haunter/M +haunting/Y +hauteur/M +have/MGS +haven't +haven/SM +haversack/SM +havoc/M +haw/GSMD +hawk/MDRZGS +hawker/M +hawkish/P +hawkishness/M +hawser/SM +hawthorn/MS +hay/GSMD +haycock/SM +hayloft/SM +haymaker/S +haymaking +haymow/SM +hayrick/MS +hayride/MS +hayseed/MS +haystack/SM +haywire +hazard/SMDG +hazardous/Y +haze/MZGJDRS +hazel/SM +hazelnut/MS +hazer/M +hazily +haziness/M +hazing/M +hazmat +hazy/RTP +hdqrs +he'd +he'll +he/M +head/MDRZGJS +headache/MS +headband/MS +headbanger/S +headbanging +headboard/SM +headbutt/DSG +headcase/S +headcheese +headcount/S +headdress/MS +header/M +headfirst +headgear/M +headhunt/DRSZG +headhunter/M +headhunting/M +headily +headiness/M +heading/M +headlamp/MS +headland/MS +headless +headlight/MS +headline/MZGDRS +headliner/M +headlock/MS +headlong +headman/M +headmaster/SM +headmen +headmistress/MS +headphone/MS +headpiece/MS +headpin/SM +headquarter/SDG +headquarters/M +headrest/MS +headroom/M +headscarf +headscarves +headset/SM +headship/SM +headshrinker/SM +headsman/M +headsmen +headstall/SM +headstand/SM +headstone/SM +headstrong +headteacher/S +headwaiter/SM +headwaters/M +headway/M +headwind/SM +headword/SM +heady/RTP +heal/DRHZGS +healed/U +healer/M +health/M +healthcare +healthful/PY +healthfulness/M +healthily/U +healthiness/UM +healthy/UTRP +heap/MDGS +hear/AHGJS +heard/AU +hearer/SM +hearing/AM +hearken/SGD +hearsay/M +hearse's +hearse/AS +heart/SM +heartache/MS +heartbeat/MS +heartbreak/SMG +heartbroken +heartburn/M +hearten/ESGD +heartfelt +hearth/M +hearthrug/S +hearths +hearthstone/SM +heartily +heartiness/M +heartland/MS +heartless/PY +heartlessness/M +heartrending/Y +heartsick/P +heartsickness/M +heartstrings/M +heartthrob/MS +heartwarming +heartwood/M +hearty/RSMPT +heat's +heat/ADGS +heated/U +heatedly +heater/SM +heath/MNRX +heathen/M +heathendom/M +heathenish +heathenism/M +heather/M +heaths +heating/M +heatproof +heatstroke/M +heatwave/S +heave/DRSMZG +heaven/SMY +heavenly/TR +heavens/M +heavenward/S +heaver/M +heavily +heaviness/M +heavy/RSMTP +heavyhearted +heavyset +heavyweight/MS +heck/M +heckle/DRSMZG +heckler/M +heckling/M +hectare/SM +hectic +hectically +hectogram/SM +hectometer/MS +hector/SMDG +hedge/DRSMZG +hedgehog/MS +hedgehop/S +hedgehopped +hedgehopping +hedger/M +hedgerow/SM +hedonism/M +hedonist/MS +hedonistic +heed/MDGS +heeded/U +heedful/Y +heedless/PY +heedlessness/M +heehaw/SMDG +heel/MDGS +heelless +heft/MDGS +heftily +heftiness/M +hefty/PRT +hegemonic +hegemony/M +hegira/SM +heifer/SM +height/XSMN +heighten/DG +heinous/YP +heinousness/M +heir/MS +heiress/MS +heirloom/SM +heist/SMDG +held +helical +helices +helicopter/SGMD +heliocentric +heliotrope/SM +helipad/S +heliport/MS +helium/M +helix/M +hell/M +hellbent +hellcat/MS +hellebore/M +hellfire +hellhole/MS +hellion/MS +hellish/YP +hellishness/M +hello/SM +helluva +helm/MS +helmet/SMD +helmsman/M +helmsmen +helot/SM +help/MDRZGSJ +helper/M +helpful/UY +helpfulness/M +helping/M +helpless/PY +helplessness/M +helpline/SM +helpmate/SM +helve/SM +hem/SM +hematite/M +hematologic +hematological +hematologist/MS +hematology/M +heme/M +hemiplegia +hemisphere/SM +hemispheric +hemispherical +hemline/SM +hemlock/SM +hemmed +hemmer/SM +hemming +hemoglobin/M +hemophilia/M +hemophiliac/MS +hemorrhage/MGDS +hemorrhagic +hemorrhoid/MS +hemostat/MS +hemp/MN +hemstitch/MDSG +hen/M +hence +henceforth +henceforward +henchman/M +henchmen +henna/SMDG +henpeck/GSD +hep +heparin/M +hepatic +hepatitis/M +hepatocyte/S +hepper +heppest +heptagon/MS +heptagonal +heptathlon/SM +herald/SMDG +heralded/U +heraldic +heraldry/M +herb/MS +herbaceous +herbage/M +herbal/S +herbalist/MS +herbicidal +herbicide/MS +herbivore/SM +herbivorous +herculean +herd/MDRZGS +herder/M +herdsman/M +herdsmen +here/M +hereabout/S +hereafter/SM +hereby +hereditary +heredity/M +herein +hereinafter +hereof +hereon +heresy/SM +heretic/SM +heretical +hereto +heretofore +hereunder +hereunto +hereupon +herewith +heritable/I +heritage/MS +hermaphrodite/SM +hermaphroditic +hermetic +hermetical/Y +hermit/SM +hermitage/MS +hermitian +hernia/SM +hernial +herniate/GNDS +herniation/M +hero/M +heroes +heroic/S +heroically +heroics/M +heroin/SM +heroine/SM +heroism/M +heron/SM +herpes/M +herpetologist/SM +herpetology/M +herring/MS +herringbone/M +herself +hertz/M +hesitance/M +hesitancy/M +hesitant/Y +hesitate/DSGNX +hesitating/UY +hesitation/M +hessian +hetero/SM +heterodox +heterodoxy/M +heterogeneity/M +heterogeneous/Y +heterosexual/MYS +heterosexuality/M +heuristic/MS +heuristically +heuristics/M +hew/ZGSDR +hewer/M +hex/GMDS +hexadecimal/S +hexagon/MS +hexagonal +hexagram/SM +hexameter/SM +hey +heyday/SM +hf +hgt +hgwy +hi/SD +hiatus/MS +hibachi/MS +hibernate/GNDS +hibernation/M +hibernator/MS +hibiscus/MS +hiccough/DG +hiccoughs +hiccup/GSMD +hick/MS +hickey/SM +hickory/SM +hid +hidden +hide/MZGJDRS +hideaway/SM +hidebound +hideous/YP +hideousness/M +hideout/MS +hider/M +hiding/M +hie/S +hieing +hierarchic +hierarchical/Y +hierarchy/SM +hieroglyph/M +hieroglyphic/MS +hieroglyphs +high/MRYZTP +highball/SM +highborn +highboy/MS +highbrow/SM +highchair/MS +highfalutin +highhanded/PY +highhandedness/M +highland/MRZS +highlander/M +highlight/SMDRZG +highlighter/M +highness/M +highroad/MS +highs +hightail/DSG +highway/MS +highwayman/M +highwaymen +hijab/SM +hijack/SJZGMDR +hijacker/M +hijacking/M +hike/MZGDRS +hiker/M +hiking/M +hilarious/PY +hilariousness/M +hilarity/M +hill/MS +hillbilly/SM +hilliness/M +hillock/MS +hillside/SM +hilltop/MS +hilly/PRT +hilt/MS +him/S +himself +hind/MRZS +hinder/GD +hindered/U +hindmost +hindquarter/MS +hindrance/SM +hindsight/M +hinge's +hinge/UDSG +hint/MDRZGS +hinter/M +hinterland/SM +hip/SPM +hipbath +hipbaths +hipbone/MS +hiphuggers +hipness/M +hipped +hipper +hippest +hippie/SM +hipping +hippo/SM +hippocampus +hippodrome/SM +hippopotamus/MS +hippy +hipster/MS +hiragana +hire's +hire/AGDS +hireling/MS +hirsute/P +hirsuteness/M +hiss/MDSG +hist +histamine/MS +histogram/MS +histologist/SM +histology/M +histopathology +historian/MS +historic +historical/Y +historicity/M +historiographer/MS +historiography/M +history/SM +histrionic/S +histrionically +histrionics/M +hit/SM +hitch's +hitch/UDSG +hitcher/MS +hitchhike/DRSMZG +hitchhiker/M +hither +hitherto +hitter/SM +hitting +hive/MGDS +hivemind/SM +hiya +hmm +ho/SMDRYZ +hoagie/MS +hoard/SZGMDRJ +hoarder/M +hoarding/M +hoarfrost/M +hoariness/M +hoarse/YTRP +hoarseness/M +hoary/TRP +hoax/MDRSZG +hoaxer/M +hob/SM +hobbit/S +hobble/MZGDRS +hobbler/M +hobby/SM +hobbyhorse/MS +hobbyist/SM +hobgoblin/MS +hobnail/SGMD +hobnob/S +hobnobbed +hobnobbing +hobo/MS +hoc +hock/MDSG +hockey/M +hockshop/MS +hod/SM +hodgepodge/SM +hoe/SM +hoecake/SM +hoedown/SM +hoeing +hoer/M +hog/SM +hogan/SM +hogback/SM +hogged +hogging +hoggish/Y +hogshead/SM +hogtie/DS +hogtying +hogwash/M +hoick/SGD +hoist/SGMD +hoke/GDS +hokey +hokier +hokiest +hokum/M +hold/MRJSZG +holdall/S +holder/M +holding/M +holdout/SM +holdover/SM +holdup/MS +hole/MGDS +holey +holiday/SMDG +holidaymaker/S +holiness/UM +holism +holistic +holistically +holler/MDGS +hollow/MDRYPSTG +hollowness/M +holly/SM +hollyhock/MS +holmium/M +holocaust/SM +hologram/MS +holograph/M +holographic +holographs +holography/M +hols +holster/SMDG +holy/URPT +homage/MS +hombre/MS +homburg/SM +home/MYZGDRS +homebody/SM +homeboy/SM +homecoming/SM +homegrown +homeland/MS +homeless/MP +homelessness/M +homelike +homeliness/M +homely/PRT +homemade +homemaker/SM +homemaking/M +homeopath/M +homeopathic +homeopaths +homeopathy/M +homeostasis/M +homeostatic +homeowner/MS +homepage/MS +homer/GMD +homeroom/MS +homeschooling/M +homesick/P +homesickness/M +homespun/M +homestead/SMDRZG +homesteader/M +homestretch/MS +hometown/MS +homeward/S +homework/MRZG +homewrecker/SM +homey/SMP +homeyness/M +homicidal +homicide/MS +homier +homiest +homiletic +homily/SM +hominid/SM +hominoid/S +hominy/M +homo/MS +homoerotic +homogeneity/M +homogeneous/Y +homogenization/M +homogenize/DSG +homograph/M +homographs +homologous +homology +homonym/SM +homophobia/M +homophobic +homophone/MS +homosexual/SM +homosexuality/M +hon/SZTGMDR +honcho/MS +hone/MS +honer/M +honest/EYT +honester +honesty/EM +honey/SGMD +honeybee/SM +honeycomb/MDSG +honeydew/SM +honeylocust/M +honeymoon/ZGMDRS +honeymooner/M +honeypot/S +honeysuckle/SM +honk/MDRSZG +honker/M +honky/SM +honor/ESGMDB +honorableness/M +honorably/E +honorarily +honorarium/MS +honorary +honoree/SM +honorer/SM +honorific/MS +hooch/M +hood/MDSG +hoodie/MS +hoodlum/SM +hoodoo/MDSG +hoodwink/DGS +hooey/M +hoof/MDRSZG +hook's +hook/UDSG +hookah/M +hookahs +hooker/MS +hookup/MS +hookworm/MS +hooky/M +hooligan/MS +hooliganism/M +hoop/MDSG +hoopla/M +hooray +hoosegow/SM +hoot/MDRSZG +hootenanny/SM +hooter/M +hoover/DSG +hooves +hop/SGMD +hope/MS +hopeful/PSMY +hopefulness/M +hopeless/YP +hopelessness/M +hopped +hopper/MS +hopping +hopscotch/MDSG +hora/MS +horde/DSMG +horehound/SM +horizon/SM +horizontal/SMY +hormonal +hormone/SM +horn/MDS +hornbeam +hornblende/M +hornet/MS +hornless +hornlike +hornpipe/MS +horny/TR +horologic +horological +horologist/MS +horology/M +horoscope/SM +horrendous/Y +horrible/P +horribleness/M +horribly +horrid/Y +horrific +horrifically +horrify/DSG +horrifying/Y +horror/MS +horse's +horse/UDSG +horseback/M +horsebox/S +horseflesh/M +horsefly/SM +horsehair/M +horsehide/M +horselaugh/M +horselaughs +horseless +horseman/M +horsemanship/M +horsemen +horseplay/M +horsepower/M +horseradish/MS +horseshit/! +horseshoe/DSM +horseshoeing +horsetail/SM +horsetrading +horsewhip/SM +horsewhipped +horsewhipping +horsewoman/M +horsewomen +horsey +horsier +horsiest +hortatory +horticultural +horticulturalist/S +horticulture/M +horticulturist/MS +hosanna/SM +hose/MGDS +hosepipe/S +hosier/MS +hosiery/M +hosp +hospholipase +hospice/MS +hospitable/I +hospitably/I +hospital/SM +hospitality/M +hospitalization/SM +hospitalize/DSG +host/MDSG +hostage/MS +hostel/ZGMDRS +hosteler/M +hostelry/SM +hostess/MDSG +hostile/MYS +hostilities/M +hostility/SM +hostler/MS +hot/SYP +hotbed/MS +hotblooded +hotbox/MS +hotcake/SM +hotel/SM +hotelier/MS +hotfoot/MDGS +hothead/DSM +hotheaded/YP +hotheadedness/M +hothouse/SM +hotkey/S +hotlink/S +hotness/M +hotplate/SM +hotpot/S +hots/M +hotshot/MS +hotted +hotter +hottest +hottie/S +hotting +hound/SGMD +hour/MYS +hourglass/MS +houri/SM +house's +house/ADSG +houseboat/SM +housebound +houseboy/SM +housebreak/RSZG +housebreaker/M +housebreaking/M +housebroke +housebroken +houseclean/DSG +housecleaning/M +housecoat/SM +housefly/SM +houseful/SM +household/SMRZ +householder/M +househusband/SM +housekeeper/MS +housekeeping/M +houselights/M +housemaid/SM +houseman/M +housemaster/S +housemate/S +housemen +housemistress/S +housemother/SM +houseparent/SM +houseplant/MS +houseproud +houseroom +housetop/SM +housewares/M +housewarming/SM +housewife/MY +housewives +housework/M +housing/MS +hove +hovel/SM +hover/SGD +hoverboard/MS +hovercraft/MS +how'd +how're +how/SM +howbeit +howdah/M +howdahs +howdy +however +howitzer/SM +howl/MDRSZG +howler/M +howsoever +hoyden/MS +hoydenish +hp +hr/S +ht +huarache/SM +hub/SM +hubbub/SM +hubby/SM +hubcap/SM +hubris/M +huckleberry/SM +huckster/SGMD +hucksterism/M +huddle/DSMG +hue/DSM +huff/MDSG +huffily +huffiness/M +huffy/PRT +hug/STMR +huge/YP +hugeness/M +hugged +hugging +huh +hula/MS +hulk/MSG +hull/MDRSZG +hullabaloo/SM +huller/M +hum/SM +human/SMRYTP +humane/PY +humaneness/M +humanism/M +humanist/SM +humanistic +humanitarian/MS +humanitarianism/M +humanities/M +humanity/ISM +humanization/CM +humanize/CDSG +humanizer/SM +humankind/M +humanness/M +humanoid/SM +humble/DRSZTGJP +humbleness/M +humbler/M +humbly +humbug/SM +humbugged +humbugging +humdinger/MS +humdrum/M +humeral +humeri +humerus/M +humid/Y +humidification/M +humidifier/CM +humidify/CZGDRS +humidity/M +humidor/SM +humiliate/DSGNX +humiliating/Y +humiliation/M +humility/M +hummed +hummer/SM +humming +hummingbird/SM +hummock/SM +hummocky +hummus/M +humongous +humor/SMDG +humoresque +humorist/MS +humorless/YP +humorlessness/M +humorous/PY +humorousness/M +hump/MDSG +humpback/MDS +humph/DG +humphs +humus/M +hunch/MDSG +hunchback/SMD +hundred/SMH +hundredfold +hundredth/M +hundredths +hundredweight/SM +hung +hunger/SMDG +hungover +hungrily +hungriness/M +hungry/PRT +hunk/MRSZ +hunker/DG +hunky/RT +hunt/MDRSZG +hunter/M +hunting/M +huntress/MS +huntsman/M +huntsmen +hurdle/DRSMZG +hurdler/M +hurdling/M +hurl/MDRSZG +hurler/M +hurling/M +hurrah/GMD +hurrahs +hurricane/MS +hurried/UY +hurry/DSMG +hurt/MSG +hurtful/YP +hurtfulness/M +hurtle/DSG +husband/GMDS +husbandman/M +husbandmen +husbandry/M +hush/MDSG +husk/MDRSZG +husker/M +huskily +huskiness/M +husky/PRSMT +hussar/SM +hussy/SM +hustings/M +hustle/DRSMZG +hustler/M +hut/SM +hutch/MS +huzzah/MDG +huzzahs +hwy +hyacinth/M +hyacinths +hybrid/SM +hybridism/M +hybridization/M +hybridize/DSG +hydra/SM +hydrangea/SM +hydrant/MS +hydrate's +hydrate/CGNDS +hydration/CM +hydraulic/S +hydraulically +hydraulics/M +hydro/M +hydrocarbon/MS +hydrocephalus/M +hydrochloride +hydrocortisone +hydrodynamic/S +hydrodynamics/M +hydroelectric +hydroelectrically +hydroelectricity/M +hydrofoil/MS +hydrogen/M +hydrogenate/CGDS +hydrogenation/M +hydrogenous +hydrologist/MS +hydrology/M +hydrolyses +hydrolysis/M +hydrolyze/DSG +hydrometer/SM +hydrometry/M +hydrophilic +hydrophobia/M +hydrophobic +hydrophone/SM +hydroplane/GDSM +hydroponic/S +hydroponically +hydroponics/M +hydrosphere/M +hydrotherapy/M +hydrothermal +hydrous +hydroxide/SM +hyena/SM +hygiene/M +hygienic/U +hygienically +hygienist/MS +hygrometer/SM +hying +hymen/SM +hymeneal +hymn/MDSG +hymnal/MS +hymnbook/SM +hype/MGDRS +hyperactive +hyperactivity/M +hyperbola/SM +hyperbole/M +hyperbolic +hypercritical/Y +hypercube +hyperglycemia/M +hyperinflation +hyperlink/GSMD +hypermarket/S +hypermedia/M +hyperparathyroidism +hyperplane +hypersensitive/P +hypersensitiveness/M +hypersensitivity/SM +hyperspace/S +hypertension/M +hypertensive/SM +hypertext/M +hyperthyroid/M +hyperthyroidism/M +hypertrophy/DSMG +hyperventilate/GNDS +hyperventilation/M +hypervisor/MS +hyphen/MDSG +hyphenate/XDSMGN +hyphenation/M +hypnoses +hypnosis/M +hypnotherapist/S +hypnotherapy/M +hypnotic/SM +hypnotically +hypnotism/M +hypnotist/MS +hypnotize/GDS +hypo/MS +hypoallergenic +hypochondria/M +hypochondriac/SM +hypocrisy/SM +hypocrite/MS +hypocritical/Y +hypodermic/MS +hypoglycemia/M +hypoglycemic/SM +hypotenuse/MS +hypothalami +hypothalamus/M +hypothermia/M +hypotheses +hypothesis/M +hypothesize/DSG +hypothetical/Y +hypothyroid/M +hypothyroidism/M +hyssop/M +hysterectomy/SM +hysteresis +hysteria/M +hysteric/SM +hysterical/Y +hysterics/M +i/US +iOS/M +iPad/M +iPhone/M +iPod/M +iTunes/M +iamb/MS +iambi +iambic/SM +iambus/MS +ibex/MS +ibid +ibidem +ibis/MS +ibuprofen/M +ice's +ice/CDSG +iceberg/SM +iceboat/SM +icebound +icebox/MS +icebreaker/SM +icecap/SM +iceman/M +icemen +ichthyologist/MS +ichthyology/M +icicle/SM +icily +iciness/M +icing/SM +icky/RT +icon/MS +iconic +iconoclasm/M +iconoclast/SM +iconoclastic +iconography/M +ictus/M +icy/TPR +id/SMY +idea/MS +ideal/SMY +idealism/M +idealist/SM +idealistic +idealistically +idealization/MS +idealize/DSG +idem +idempotent +identical/Y +identifiable/U +identification/M +identified/U +identify/ZGNDRSX +identikit/S +identity/SM +ideogram/SM +ideograph/M +ideographs +ideological/Y +ideologist/SM +ideologue/MS +ideology/SM +ides/M +idiocy/SM +idiom/SM +idiomatic/U +idiomatically +idiopathic +idiosyncrasy/SM +idiosyncratic +idiosyncratically +idiot/SM +idiotic +idiotically +idle/MZTGDRSP +idleness/M +idler/M +idol/MS +idolater/SM +idolatress/MS +idolatrous +idolatry/M +idolization/M +idolize/GDS +idyll/SM +idyllic +idyllically +if/SM +iffiness/M +iffy/RTP +igloo/SM +igneous +ignitable +ignite/AGDS +ignition/MS +ignoble +ignobly +ignominious/Y +ignominy/SM +ignoramus/MS +ignorance/M +ignorant/Y +ignore/GDS +iguana/MS +ii +iii +ilea +ileitis/M +ileum/M +ilia +ilium/M +ilk/SM +ill/SMP +illegal/MYS +illegality/SM +illegibility/M +illegible +illegibly +illegitimacy/M +illegitimate/Y +illiberal/Y +illiberality/M +illicit/YP +illicitness/M +illimitable +illiteracy/M +illiterate/MYS +illness/MS +illogical/Y +illogicality/M +illuminate/GNXDS +illuminating/Y +illumination/M +illumine/DSBG +illus/V +illusion/EMS +illusionist/SM +illusory +illustrate/GNVXDS +illustration/M +illustrative/Y +illustrator/SM +illustrious/PY +illustriousness/M +image/DSMG +imagery/M +imaginable/U +imaginably/U +imaginal +imaginary +imagination/MS +imaginative/UY +imagine/DSBJG +imago/M +imagoes +imam/MS +imbalance/DSM +imbecile/MS +imbecilic +imbecility/SM +imbibe/ZGDRS +imbiber/M +imbrication/M +imbroglio/SM +imbue/DSG +imitable/I +imitate/DSGNVX +imitation/M +imitative/PY +imitativeness/M +imitator/SM +immaculate/PY +immaculateness/M +immanence/M +immanency/M +immanent/Y +immaterial/YP +immateriality/M +immaterialness/M +immature/Y +immaturity/M +immeasurable +immeasurably +immediacies/M +immediacy/SM +immediate/PY +immediateness/M +immemorial/Y +immense/Y +immensity/SM +immerse/XDSGNV +immersible +immersion/M +immigrant/SM +immigrate/DSGN +immigration/M +imminence/M +imminent/Y +immobile +immobility/M +immobilization/M +immobilize/ZGDRS +immoderate/Y +immodest/Y +immodesty/M +immolate/DSGN +immolation/M +immoral/Y +immorality/SM +immortal/MYS +immortality/M +immortalize/DSG +immovability/M +immovable +immovably +immune +immunity/M +immunization/SM +immunize/GDS +immunodeficiency/M +immunodeficient +immunoglobulin/S +immunologic +immunological +immunologist/MS +immunology/M +immure/DSG +immutability/M +immutable +immutably +imp/SMR +impact/SMDG +impair/SDGL +impaired/U +impairment/MS +impala/SM +impale/DSGL +impalement/M +impalpable +impalpably +impanel/SDG +impart/SDG +impartial/Y +impartiality/M +impassably +impasse/BSMV +impassibility/M +impassible +impassibly +impassioned +impassive/YP +impassiveness/M +impassivity/M +impasto/M +impatience/MS +impatiens/M +impatient/Y +impeach/ZGBLDRS +impeachable/U +impeacher/M +impeachment/SM +impeccability/M +impeccable +impeccably +impecunious/PY +impecuniousness/M +impedance/M +impede/DSG +impeded/U +impediment/SM +impedimenta/M +impel/S +impelled +impeller/MS +impelling +impend/SDG +impenetrability/M +impenetrable +impenetrably +impenitence/M +impenitent/Y +imperative/SMY +imperceptibility/M +imperceptible +imperceptibly +imperceptive +imperf +imperfect/SMYP +imperfection/MS +imperfectness/M +imperial/MYS +imperialism/M +imperialist/SM +imperialistic +imperialistically +imperil/GSLD +imperilment/M +imperious/PY +imperiousness/M +imperishable +imperishably +impermanence/M +impermanent/Y +impermeability/M +impermeable +impermeably +impermissible +impersonal/Y +impersonate/GNXDS +impersonation/M +impersonator/SM +impertinence/MS +impertinent/Y +imperturbability/M +imperturbable +imperturbably +impervious/Y +impetigo/M +impetuosity/M +impetuous/YP +impetuousness/M +impetus/MS +impiety/SM +impinge/LDSG +impingement/M +impious/PY +impiousness/M +impish/YP +impishness/M +implacability/M +implacable +implacably +implant/BSGMD +implantation/M +implausibility/SM +implausible +implausibly +implement/GBMDRS +implementable/U +implementation/SM +implemented/U +implicate/DSG +implication/M +implicit/PY +implicitness/M +implode/DSG +implore/DSG +imploring/Y +implosion/MS +implosive +imply/XDSGN +impolite/YP +impoliteness/MS +impolitic +imponderable/MS +import/ZGBSMDR +importance/M +important/Y +importation/MS +importer/M +importunate/Y +importune/GDS +importunity/M +impose/ADSG +imposer/MS +imposing/U +imposingly +imposition/MS +impossibility/SM +impossible/S +impossibly +impost/SM +impostor/SM +imposture/MS +impotence/M +impotency/M +impotent/Y +impound/DGS +impoverish/DSLG +impoverishment/M +impracticability +impracticable +impracticably +impractical/Y +impracticality/M +imprecate/DSXGN +imprecation/M +imprecise/PYN +impreciseness/M +imprecision/M +impregnability/M +impregnable +impregnably +impregnate/GNDS +impregnation/M +impresario/SM +impress/MDSGV +impressed/U +impressibility/M +impressible +impression/BSM +impressionability/M +impressionism/M +impressionist/SM +impressionistic +impressive/PY +impressiveness/M +imprimatur/SM +imprint/MDRZGS +imprinter/M +imprison/SDLG +imprisonment/SM +improbability/SM +improbable +improbably +impromptu/SM +improper/Y +impropriety/SM +improve/GBDSL +improved/U +improvement/MS +improvidence/M +improvident/Y +improvisation/SM +improvisational +improvise/ZGDRS +improviser/M +imprudence/M +imprudent/Y +impudence/M +impudent/Y +impugn/ZGSDR +impugner/M +impulse/MGNVDS +impulsion/M +impulsive/PY +impulsiveness/M +impulsivity +impunity/M +impure/RYT +impurity/SM +imputation/SM +impute/BDSG +in/ASM +inaccuracy/S +inaction/M +inadequacy/S +inadvertence/M +inadvertent/Y +inalienability/M +inalienably +inamorata/SM +inane/RYT +inanimate/PY +inanimateness/M +inanity/SM +inappropriate/Y +inarticulate/Y +inasmuch +inaudible +inaugural/SM +inaugurate/XGNDS +inauguration/M +inboard/MS +inbound +inbox/MS +inbreed/S +inc/TGD +incalculably +incandescence/M +incandescent/Y +incantation/SM +incapacitate/GNDS +incarcerate/XDSGN +incarceration/M +incarnadine/DSG +incarnate/AXGNDS +incarnation/AM +incendiary/SM +incense/MGDS +incentive's +incentive/ES +inception/SM +incessant/Y +incest/M +incestuous/PY +incestuousness/M +inch/MDSG +inchoate +inchworm/SM +incidence/SM +incident/SM +incidental/MYS +incinerate/DSGN +incineration/M +incinerator/MS +incipience/M +incipient/Y +incise/XGNVDS +incision/M +incisive/PY +incisiveness/M +incisor/MS +incitement/MS +inciter/MS +incl +inclement +inclination/EM +inclinations +incline's +incline/EGDS +include/GDS +inclusion/MS +inclusive/YP +inclusiveness/M +incognito/MS +incombustible +incommode/GD +incommodious +incommunicado +incompatibility/S +incompetent/MS +incomplete/Y +inconceivability/M +incongruous/PY +incongruousness/M +inconsolably +inconstant/Y +incontestability/M +incontestably +incontinent +incontrovertibly +inconvenience/GD +incorporate/ADSGN +incorporated/U +incorporation/AM +incorporeal +incorrect/Y +incorrigibility/M +incorrigible +incorrigibly +incorruptibly +increasing/Y +increment/SMDG +incremental/Y +incrementalism +incrementalist/SM +incriminate/GNDS +incrimination/M +incriminatory +incrustation/SM +incubate/GNDS +incubation/M +incubator/SM +incubus/MS +inculcate/DSGN +inculcation/M +inculpate/DSG +incumbency/SM +incumbent/SM +incunabula +incunabulum/M +incur/SB +incurable/MS +incurably +incurious +incurred +incurring +incursion/MS +ind +indebted/P +indebtedness/M +indeed +indefatigable +indefatigably +indefeasible +indefeasibly +indefinably +indelible +indelibly +indemnification/M +indemnify/GDSXN +indemnity/SM +indentation/MS +indention/M +indenture/DG +indescribably +indestructibly +indeterminably +indeterminacy/M +indeterminate/Y +index/ZGMDRS +indexation/SM +indexer/M +indicate/XDSGNV +indication/M +indicative/SMY +indicator/MS +indict/GDSBL +indictment/SM +indie/S +indigence/M +indigenous +indigent/SMY +indignant/Y +indignation/M +indigo/M +indirect/Y +indiscipline +indiscreet/Y +indiscretion/S +indiscriminate/Y +indispensability/M +indispensable/MS +indispensably +indissolubility +indissolubly +indistinguishably +indite/GDS +indium/M +individual/MYS +individualism/M +individualist/MS +individualistic +individualistically +individuality/M +individualization/M +individualize/GDS +individuate/DSGN +individuation/M +indivisibly +indoctrinate/GNDS +indoctrination/M +indolence/M +indolent/Y +indomitable +indomitably +indubitable +indubitably +induce/DRSZGL +inducement/SM +inducer/M +induct/DGV +inductance/M +inductee/SM +induction/MS +inductive/Y +indulge/DSG +indulgence/SM +indulgent/Y +industrial/Y +industrialism/M +industrialist/SM +industrialization/M +industrialize/DSG +industrious/YP +industriousness/M +industry/SM +indwell/SG +inebriate/MGNDS +inebriation/M +inedible +ineffability/M +ineffable +ineffably +inelastic +ineligible/MS +ineligibly +ineluctable +ineluctably +inept/YP +ineptitude/M +ineptness/M +inequality/S +inert/YP +inertia/M +inertial +inertness/M +inescapable +inescapably +inestimably +inevitability/M +inevitable/M +inevitably +inexact/Y +inexhaustibly +inexorability +inexorable +inexorably +inexpedient +inexpert/Y +inexpiable +inexplicably +inexpressibly +inexpressive +inextricably +inf/ZT +infallible +infamy/SM +infancy/M +infant/MS +infanticide/MS +infantile +infantry/SM +infantryman/M +infantrymen +infarct/MS +infarction/M +infatuate/DSXGN +infatuation/M +infect/AESDG +infected/U +infection/ASM +infectious/PY +infectiousness/M +infelicitous +inference/SM +inferential +inferior/MS +inferiority/M +infernal/Y +inferno/MS +inferred +inferring +infest/GDS +infestation/MS +infidel/MS +infidelity/S +infiltrator/SM +infinite/MV +infinitesimal/SMY +infinitival +infinitive/MS +infinitude/M +infinity/SM +infirm +infirmary/SM +infirmity/SM +infix +inflame/DSG +inflammable +inflammation/SM +inflammatory +inflatable/SM +inflate/ADSG +inflation/EM +inflationary +inflect/SDG +inflection/MS +inflectional +inflict/SDGV +infliction/M +inflow/SM +influence/MGDS +influenced/U +influential/Y +influenza/M +info/M +infomercial/SM +inform/Z +informal/Y +informant/SM +informatics +information/EM +informational +informative/PY +informativeness/M +informed/U +infotainment/M +infra +infrared/M +infrasonic +infrastructural +infrastructure/SM +infrequence/M +infrequent/Y +infringement/MS +infuriate/GDS +infuriating/Y +infuser/SM +ingenious/PY +ingeniousness/M +ingenue/SM +ingenuity/M +ingenuous/EY +ingenuousness/M +ingest/SDG +ingestion/M +inglenook/SM +ingot/SM +ingrain/G +ingrate/SM +ingratiate/GNDS +ingratiating/Y +ingratiation/M +ingredient/MS +ingress/MS +inguinal +inhabit/DG +inhabitable/U +inhabitant/SM +inhabited/U +inhalant/SM +inhalation/MS +inhalator/MS +inhaler/SM +inharmonious +inhere/DSG +inherent/Y +inherit/EGSD +inheritance/EM +inheritances +inheritor/SM +inhibit/GSD +inhibition/SM +inhibitor/SM +inhibitory +inhuman/Y +inhumane/Y +inimical/Y +inimitably +iniquitous/Y +iniquity/SM +initial/SGMDY +initialism +initialization +initialize/DSG +initialized/AU +initiate/XMGNVDS +initiated/U +initiation/M +initiative/SM +initiator/MS +initiatory +initio +inject/SDG +injection/SM +injector/SM +injunctive +injure/DRSZG +injured/U +injurer/M +injurious +ink/MD +inkblot/SM +inkiness/M +inkling/SM +inkstand/SM +inkwell/MS +inky/RTP +inland/M +inline +inmate/SM +inmost +inn/SGMRJ +innards/M +innate/PY +innateness/M +innermost +innersole/SM +innerspring +innervate/GNDS +innervation/M +inning/M +innit +innkeeper/MS +innocence/M +innocent/MYS +innocuous/PY +innocuousness/M +innovate/XDSGNV +innovation/M +innovator/MS +innovatory +innuendo/SM +innumerably +innumerate +inoculate/AGDS +inoculation/MS +inoperative +inordinate/Y +inorganic +inositol +inquire/ZGDR +inquirer/M +inquiring/Y +inquiry/SM +inquisition/MS +inquisitional +inquisitive/YP +inquisitiveness/M +inquisitor/SM +inquisitorial +inrush/MS +insane/T +insatiability/M +insatiably +inscribe/ZGDR +inscriber/M +inscription/MS +inscrutability/M +inscrutable/P +inscrutableness/M +inscrutably +inseam/SM +insecticidal +insecticide/MS +insectivore/MS +insectivorous +insecure/Y +inseminate/DSGN +insemination/M +insensate +insensible +insensitive/Y +inseparable/MS +insert's +insert/AGSD +insertion/AM +insertions +insetting +inshore +inside/RSMZ +insider/M +insidious/YP +insidiousness/M +insight/MS +insightful +insignia/M +insinuate/GNVDSX +insinuation/M +insinuator/SM +insipid/PY +insipidity/M +insist/SGD +insistence/M +insistent/Y +insisting/Y +insofar +insole/SM +insolence/M +insolent/Y +insoluble +insolubly +insolvency/S +insomnia/M +insomniac/SM +insomuch +insouciance/M +insouciant +inspect/AGDS +inspection/SM +inspector/MS +inspectorate/MS +inspiration/MS +inspirational +inspiratory +inspired/U +inspiring/U +inst +instability/S +install/UBZRSDG +installation/MS +installer/UM +installment/SM +instance/GD +instant/MRYS +instantaneous/Y +instantiate/DSG +instar +instate/AGDS +instead +instigate/DSGN +instigation/M +instigator/MS +instillation/M +instinct/VMS +instinctive/Y +instinctual +institute/XMZGNDRS +instituter/M +institution/M +institutional/Y +institutionalization/M +institutionalize/DSG +instr +instruct/SDGV +instructed/U +instruction/MS +instructional +instructive/Y +instructor/MS +instrument/MDSG +instrumental/MYS +instrumentalist/SM +instrumentality/M +instrumentation/M +insubordinate +insufferable +insufferably +insula +insular +insularity/M +insulate/GNDS +insulation/M +insulator/MS +insulin/M +insult/SMDG +insulting/Y +insuperable +insuperably +insurance/SM +insure/DRSZGB +insured/SM +insurer/M +insurgence/SM +insurgency/SM +insurgent/MS +insurmountably +insurrection/SM +insurrectionist/SM +int +intact +intaglio/MS +integer/MS +integral/SMY +integrate/AEVNGSD +integration/EAM +integrator +integrity/M +integument/SM +intel/M +intellect/MS +intellectual/MYS +intellectualism/M +intellectualize/GDS +intelligence/M +intelligent/Y +intelligentsia/M +intelligibility/M +intelligible/U +intelligibly/U +intended/SM +intense/YTVR +intensification/M +intensifier/M +intensify/DRSZGN +intensity/S +intensive/MYPS +intensiveness/M +intent/SMYP +intention/MS +intentional/UY +intentness/M +inter/ESL +interact/SGVD +interaction/SM +interactive/Y +interactivity +interbred +interbreed/GS +intercede/GDS +intercept/GMDS +interception/MS +interceptor/SM +intercession/SM +intercessor/MS +intercessory +interchange/DSMG +interchangeability +interchangeable +interchangeably +intercity +intercollegiate +intercom/SM +intercommunicate/DSGN +intercommunication/M +interconnect/GDS +interconnection/SM +intercontinental +intercourse/M +intercultural +interdenominational +interdepartmental +interdependence/M +interdependent/Y +interdict/GMDS +interdiction/M +interdisciplinary +interest/ESMD +interested/U +interesting/Y +interface/MGDS +interfaith +interfere/GDS +interference/M +interferon/M +interfile/GDS +intergalactic +intergovernmental +interim/M +interior/SM +interj +interject/GDS +interjection/SM +interlace/GDS +interlard/DGS +interleave/DSG +interleukin/M +interline/GDSJ +interlinear +interlining/M +interlink/DSG +interlock/GMDS +interlocutor/SM +interlocutory +interlope/ZGDRS +interloper/M +interlude/MGDS +intermarriage/SM +intermarry/GDS +intermediary/SM +intermediate/MYS +interment/EM +interments +intermezzi +intermezzo/MS +interminably +intermingle/DSG +intermission/SM +intermittence +intermittency +intermittent/Y +intermix/GDS +intern/GDL +internal/SY +internalization/M +internalize/GDS +international/SMY +internationalism/M +internationalist/SM +internationalization +internationalize/DSG +internecine +internee/SM +internet +internist/MS +internment/M +internship/MS +interoffice +interoperability +interoperable +interoperate/S +interpenetrate/DSGN +interpersonal +interplanetary +interplay/M +interpolate/XDSGN +interpolation/M +interpose/GDS +interposition/M +interpret/AGVDS +interpretation/AMS +interpretative +interpreted/U +interpreter/MS +interracial +interred/E +interregnum/SM +interrelate/XDSGN +interrelation/M +interrelationship/MS +interring/E +interrogate/DSGNVX +interrogation/M +interrogative/MYS +interrogator/SM +interrogatory/SM +interrupt/ZGMDRS +interrupter/M +interruption/MS +interscholastic +intersect/GDS +intersection/SM +intersectional +intersectionality +intersession/SM +intersex +intersperse/GNDS +interspersion/M +interstate/MS +interstellar +interstice/MS +interstitial +intertwine/GDS +interurban +interval/SM +intervene/GDS +intervention/SM +interventionism/M +interventionist/SM +interview/ZGMDRS +interviewee/MS +interviewer/M +intervocalic +interwar +interweave/GS +interwove +interwoven +intestacy/M +intestate +intestinal +intestine/MS +intifada +intimacy/SM +intimate/MYGNDSX +intimation/M +intimidate/GNDS +intimidating/Y +intimidation/M +intonation/SM +intoxicant/SM +intoxicate/DSGN +intoxication/M +intracranial +intramural +intramuscular +intranet/MS +intransigence/M +intransigent/MYS +intrastate +intrauterine +intravenous/MSY +intrepid/Y +intrepidity/M +intricacy/SM +intricate/Y +intrigue/DRSMZG +intriguer/M +intriguing/Y +intrinsic +intrinsically +intro/SM +introduce/AGDS +introduction/AM +introductions +introductory +introit/SM +introspect/GVDS +introspection/M +introspective/Y +introversion/M +introvert/MDS +intrude/DRSZG +intruder/M +intrusion/SM +intrusive/YP +intrusiveness/M +intuit/SDGV +intuition/S +intuitive/PY +intuitiveness/M +inundate/XDSGN +inundation/M +inure/DSG +invade/DRSZG +invader/M +invalid/GMDYS +invalidism/M +invaluable +invaluably +invariant +invasion/MS +invasive +invective/M +inveigh/GD +inveighs +inveigle/ZGDRS +inveigler/M +invent/ASGVD +invention/AMS +inventive/PY +inventiveness/M +inventor/MS +inventory/DSMG +inverse/SMY +invert/SMDRZG +inverter/M +invest/ASDGL +investigate/GNVDSX +investigation/M +investigator/SM +investigatory +investiture/MS +investment/AEM +investor/SM +inveteracy/M +inveterate +invidious/YP +invidiousness/M +invigilate/GNDS +invigilator/S +invigorate/ADSG +invigorating/Y +invigoration/M +invincibility/M +invincibly +inviolability/M +inviolably +inviolate +invitation/SM +invitational/SM +invite/DSMG +invited/U +invitee/SM +inviting/Y +invoke/DSG +involuntariness/M +involuntary/P +involution/M +involve/LDSG +involved/U +involvement/SM +inward/SY +ioctl +iodide/SM +iodine/M +iodize/DSG +ion/USM +ionic +ionization/UM +ionize/UDSG +ionizer/MS +ionosphere/MS +ionospheric +iota/MS +ipecac/SM +irascibility/M +irascible +irascibly +irate/YP +irateness/M +ire/M +ireful +irenic +irides +iridescence/M +iridescent/Y +iridium/M +iris/MS +irk/SGD +irksome/YP +irksomeness/M +iron/MDSG +ironclad/MS +ironic +ironical/Y +ironing/M +ironmonger/S +ironmongery +ironstone/M +ironware/M +ironwood/MS +ironwork/M +irony/SM +irradiate/DSGN +irradiation/M +irrational/SMY +irrationality/M +irreclaimable +irreconcilability/M +irreconcilable +irreconcilably +irrecoverable +irrecoverably +irredeemable +irredeemably +irreducible +irreducibly +irrefutable +irrefutably +irregular/MYS +irregularity/SM +irrelevance/MS +irrelevancy/MS +irrelevant/Y +irreligion +irreligious +irremediable +irremediably +irremovable +irreparable +irreparably +irreplaceable +irrepressible +irrepressibly +irreproachable +irreproachably +irresistible +irresistibly +irresolute/PYN +irresoluteness/M +irresolution/M +irrespective +irresponsibility/M +irresponsible +irresponsibly +irretrievable +irretrievably +irreverence/M +irreverent/Y +irreversible +irreversibly +irrevocable +irrevocably +irrigable +irrigate/DSGN +irrigation/M +irritability/M +irritable +irritably +irritant/SM +irritate/DSXGN +irritating/Y +irritation/M +irrupt/DGVS +irruption/SM +ischemia +ischemic +isinglass/M +isl +island/SZMR +islander/M +isle/MS +islet/SM +ism/CM +isms +isn't +isobar/MS +isobaric +isolate/DSMGN +isolation/M +isolationism/M +isolationist/SM +isomer/MS +isomeric +isomerism/M +isometric/S +isometrically +isometrics/M +isomorphic +isomorphism +isosceles +isotherm/SM +isotope/SM +isotopic +isotropic +issuance/M +issue/ADSMG +issuer/MS +isthmian +isthmus/MS +it'd +it'll +it/USM +ital +italic/SM +italicization/M +italicize/GDS +italics/M +itch/MDSG +itchiness/M +itchy/RPT +item/MS +itemization/M +itemize/GDS +iterate/AXGNVDS +iteration/AM +iterator/S +itinerant/SM +itinerary/SM +itself +iv/U +ivory/SM +ivy/DSM +ix +j/F +jab/SM +jabbed +jabber/SMDRZG +jabberer/M +jabbing +jabot/SM +jacaranda/MS +jack/MDGS +jackal/SM +jackass/MS +jackboot/SMD +jackdaw/MS +jacket/SMD +jackhammer/MS +jackknife/MGDS +jackknives +jackpot/MS +jackrabbit/MS +jackstraw/MS +jacquard/M +jade/MGDS +jaded/PY +jadedness/M +jadeite/M +jag/SM +jagged/TPRY +jaggedness/M +jaggies +jaguar/SM +jail/MDRZGS +jailbird/SM +jailbreak/SM +jailer/M +jailhouse/S +jalapeno/MS +jalopy/SM +jalousie/MS +jam/SM +jamb/MS +jambalaya/M +jamboree/MS +jammed +jamming +jammy/RT +jangle/DRSMZG +jangler/M +janitor/SM +janitorial +japan/SM +japanned +japanning +jape/MGDS +jar/SM +jardiniere/SM +jarful/MS +jargon/M +jarred +jarring/Y +jasmine/SM +jasper/M +jato/MS +jaundice/DSMG +jaunt/SGMD +jauntily +jauntiness/M +jaunty/RPT +java/M +javelin/SM +jaw/SGMD +jawbone/DSMG +jawbreaker/MS +jawline/S +jay/SM +jaybird/SM +jaywalk/DRSZG +jaywalker/M +jaywalking/M +jazz/MDSG +jazzy/TR +jct +jealous/Y +jealousy/SM +jean/MS +jeans/M +jeep/MS +jeer/MDSG +jeering/MY +jeez +jejuna +jejune +jejunum/M +jell/DSG +jello/S +jelly/GDSM +jellybean/MS +jellyfish/MS +jellylike +jellyroll/SM +jemmy/GDS +jennet/MS +jenny/SM +jeopardize/GDS +jeopardy/M +jeremiad/MS +jerk/MDSG +jerkily +jerkin/MS +jerkiness/M +jerkwater +jerky/TRMP +jeroboam/S +jerrybuilt +jerrycan/S +jersey/MS +jest/MDRSZG +jester/M +jesting/Y +jet/SM +jetliner/SM +jetport/MS +jetsam/M +jetted +jetting +jettison/MDSG +jetty/SM +jewel/SZGMDR +jeweler/M +jewelry/SM +jg +jib/SGMD +jibbed +jibbing +jibe/MS +jiff/MS +jiffy/SM +jig's +jig/AS +jigged/A +jigger's +jigger/ASDG +jigging/A +jiggle/DSMG +jiggly +jigsaw/SMDG +jihad/SM +jihadist/SM +jilt/MDSG +jimmy/DSMG +jimsonweed/M +jingle/DSMG +jingly +jingoism/M +jingoist/SM +jingoistic +jink/DSG +jinn +jinni/M +jinrikisha/SM +jinx/MDSG +jitney/SM +jitterbug/MS +jitterbugged +jitterbugger/M +jitterbugging +jitters/M +jittery/RT +jive/MGDS +job/SM +jobbed +jobber/SM +jobbing +jobholder/MS +jobless/P +joblessness/M +jobshare/S +jobsworth +jobsworths +jock/MS +jockey/SGMD +jockstrap/MS +jocose/PY +jocoseness/M +jocosity/M +jocular/Y +jocularity/M +jocund/Y +jocundity/M +jodhpurs/M +joey/S +jog/SM +jogged +jogger/SM +jogging/M +joggle/DSMG +john/MS +johnny/SM +johnnycake/MS +join's +join/AFDSG +joiner/FMS +joinery/M +joint's +joint/EGSD +jointly/F +joist/SM +jojoba +joke/MZGDRS +joker/M +jokey +jokier +jokiest +joking/Y +jollification/SM +jollily +jolliness/M +jollity/M +jolly/TGPDRSM +jolt/MDRSZG +jolter/M +jonquil/SM +josh/MDRSZG +josher/M +jostle/MGDS +jot/SM +jotted +jotter/MS +jotting/MS +joule/SM +jounce/MGDS +jouncy +journal/MS +journalese/M +journalism/M +journalist/SM +journalistic +journey/ZGMDRS +journeyer/M +journeyman/M +journeymen +journo/S +joust/SZGMDR +jouster/M +jousting/M +jovial/Y +joviality/M +jowl/MS +jowly/TR +joy/SGMD +joyful/YP +joyfuller +joyfullest +joyfulness/M +joyless/PY +joylessness/M +joyous/YP +joyousness/M +joyridden +joyride/RSMZG +joyrider/M +joyriding/M +joyrode +joystick/SM +jr +jubilant/Y +jubilation/M +jubilee/SM +judder/GDS +judge's +judge/ADSG +judgeship/M +judgment/SM +judgmental/Y +judicatory/SM +judicature/M +judicial/Y +judiciary/SM +judicious/IYP +judiciousness/IM +judo/M +jug/SM +jugful/MS +jugged +juggernaut/SM +jugging +juggle/MZGDRS +juggler/M +jugglery/M +jugular/SM +juice/DRSMZG +juicer/M +juicily +juiciness/M +juicy/PTR +jujitsu/M +jujube/MS +jukebox/MS +julep/SM +julienne +jumble/MGDS +jumbo/SM +jump/MDRSZG +jumper/M +jumpily +jumpiness/M +jumpsuit/MS +jumpy/TRP +jun +junco/SM +junction/FISM +juncture/FMS +jungle/MS +junior/MS +juniper/SM +junk/MDRSZG +junker/M +junket/MDSG +junketeer/MS +junkie/MTRS +junkyard/MS +junta/SM +juridic +juridical/Y +jurisdiction/SM +jurisdictional +jurisprudence/M +jurist/MS +juristic +juror/SM +jury/ISM +juryman/M +jurymen +jurywoman/M +jurywomen +just/RYPT +justice/IMS +justifiable/U +justifiably/U +justification/M +justified/U +justify/XGDSN +justness/M +jut/SM +jute/M +jutted +jutting +juvenile/SM +juxtapose/DSG +juxtaposition/SM +k/IFGS +kHz +kW +kWh +kabbalah +kaboom +kabuki/M +kaddish/MS +kaffeeklatch/MS +kaffeeklatsch/MS +kahuna/S +kaiser/MS +kale/M +kaleidoscope/MS +kaleidoscopic +kaleidoscopically +kamikaze/MS +kana +kangaroo/MS +kanji +kaolin/M +kapok/M +kappa/SM +kaput +karakul/M +karaoke/MS +karat/SM +karate/M +karma/M +karmic +kart/MS +katakana +katydid/SM +kayak/SMDG +kayaking/M +kayo/MDSG +kazoo/SM +kc +kebab/SM +kedgeree +keel/MDSG +keelhaul/DGS +keen/MDRYSTGP +keenness/M +keep/MRSZG +keeper/M +keeping/M +keepsake/MS +keg/SM +kelp/M +kelvin/SM +ken/SM +kenned +kennel/SGMD +kenning +keno/M +kepi/MS +kept +keratin/M +keratitis +kerbside +kerchief/SM +kerfuffle/S +kernel/SM +kerosene/M +kestrel/MS +ketch/MS +ketchup/M +keto +ketogenic +ketone/S +kettle/SM +kettledrum/SM +key/SGMD +keybinding/S +keyboard/ZGSMDR +keyboarder/M +keyboardist/SM +keyhole/MS +keynote/MZGDRS +keynoter/M +keypad/SM +keypunch/ZGMDRS +keypuncher/M +keystone/MS +keystroke/SM +keyword/MS +kg +khaki/SM +khan/MS +kibble/DSMG +kibbutz/MS +kibbutzim +kibitz/ZGDRS +kibitzer/M +kibosh/M +kick/MDRSZG +kickback/SM +kickball/M +kickboxing +kicker/M +kickoff/MS +kickstand/MS +kicky/RT +kid/SM +kidded +kidder/SM +kiddie/SM +kidding +kiddish +kiddo/SM +kidnap/S +kidnapped +kidnapper/MS +kidnapping/MS +kidney/SM +kidskin/M +kielbasa/MS +kielbasi +kike/S +kill/JMDRSZG +killdeer/SM +killer/M +killing/M +killjoy/SM +kiln/MDSG +kilo/MS +kilobyte/SM +kilocoulomb/S +kilocycle/SM +kilogram/SM +kilohertz/M +kilojoule/S +kiloliter/MS +kilometer/MS +kilonewton/S +kilopascal/S +kiloton/SM +kilovolt/S +kilowatt/SM +kilt/MDRS +kilter/M +kimono/MS +kin/M +kinase +kind's +kind/UPRYT +kinda +kindergarten/MS +kindergartner/SM +kindhearted/PY +kindheartedness/M +kindle/AGDS +kindliness/M +kindling/M +kindly/URT +kindness/UM +kindnesses +kindred/M +kinds +kine/S +kinematic/S +kinematics/M +kinetic/S +kinetically +kinetics/M +kinfolk/SM +kinfolks/M +king/MYS +kingdom/SM +kingfisher/SM +kingly/RT +kingmaker/S +kingpin/SM +kingship/M +kink/MDSG +kinkily +kinkiness/M +kinky/TPR +kinsfolk/M +kinship/M +kinsman/M +kinsmen +kinswoman/M +kinswomen +kiosk/SM +kip/SM +kipped +kipper/MDGS +kipping +kirsch/MS +kismet/M +kiss/MDRSBZG +kisser/M +kissoff/SM +kissogram/S +kit/SGMD +kitchen/SM +kitchenette/MS +kitchenware/M +kite/MS +kith/M +kitsch/M +kitschy +kitted +kitten/MS +kittenish +kitting +kitty/SM +kiwi/MS +kiwifruit/MS +kl +klaxon/S +kleptocracy +kleptomania/M +kleptomaniac/SM +kludge/GDS +kluge/DS +klutz/MS +klutziness/M +klutzy/TRP +km +kn +knack/SZMR +knacker/GD +knapsack/MS +knave/SM +knavery/M +knavish/Y +knead/SZGDR +kneader/M +knee/MDS +kneecap/SM +kneecapped +kneecapping +kneeing +kneel/SG +knell/SGMD +knelt +knew +knicker/S +knickerbockers/M +knickers/M +knickknack/MS +knife/DSMG +knight/MDYSG +knighthood/MS +knightliness/M +knish/MS +knit/MS +knitted +knitter/SM +knitting/M +knitwear/M +knives +knob/MS +knobbly +knobby/TR +knock/SZGMDR +knockabout +knockdown/SM +knocker/M +knockoff/SM +knockout/SM +knockwurst/SM +knoll/SM +knot/MS +knothole/SM +knotted +knotting +knotty/TR +know/SB +knowing/UYS +knowledge/M +knowledgeable +knowledgeably +known +knuckle/DSMG +knuckleduster/S +knucklehead/MS +knurl/SGMD +koala/SM +koan/S +kohl +kohlrabi/M +kohlrabies +kola/MS +kook/MS +kookaburra/SM +kookiness/M +kooky/TPR +kopeck/MS +korma +kosher/DSG +kowtow/GMDS +kph +kraal/SM +kraut/SM! +krill/M +krona/M +krone/RM +kronor +kronur +krypton/M +kt +kuchen/SM +kudos/M +kudzu/SM +kumquat/MS +kvetch/ZGMDRS +kvetcher/M +kw +l/SDXTGJ +la/M +lab/SM +label's +label/ASDG +labeled/U +labia +labial/SM +labile +labium/M +labor/SMDRZG +laboratory/SM +laborer/M +laborious/PY +laboriousness/M +laborsaving +laburnum/MS +labyrinth/M +labyrinthine +labyrinths +lac/M +lace's +lace/UGDS +lacerate/DSGNX +laceration/M +lacewing/SM +lacework/M +lachrymal +lachrymose +lack/MDSG +lackadaisical/Y +lackey/SM +lackluster +laconic +laconically +lacquer/GMDS +lacrosse/M +lactate/GNDS +lactation/M +lacteal +lactic +lactose/M +lacuna/M +lacunae +lacy/RT +lad/SGMDNJ +ladder/GSMD +laddie/SM +laddish/P +lade/S +laden/U +lading/M +ladle/DSMG +lady/SM +ladybird/SM +ladybug/MS +ladyfinger/MS +ladylike/U +ladylove/MS +ladyship/MS +laetrile/M +lag/SZMR +lager/M +laggard/MYS +lagged +lagging/M +lagniappe/SM +lagoon/SM +laid/IA +lain +lair/MS +laird/SM +laity/M +lake/MS +lakefront/S +lakeside +lam/SM +lama/MS +lamasery/SM +lamb/MDSG +lambada/MS +lambaste/GDS +lambda/SM +lambency/M +lambent/Y +lambkin/SM +lambskin/SM +lambswool +lame/MYZTGDRSP +lamebrain/MDS +lameness/M +lament/BSMDG +lamentably +lamentation/MS +lamina/M +laminae +laminar +laminate/MGNDS +lamination/M +lammed +lamming +lamp/MS +lampblack/M +lamplight/MRZ +lamplighter/M +lampoon/SGMD +lamppost/SM +lamprey/MS +lampshade/SM +lanai/SM +lance/DRSMZG +lancer/M +lancet/SM +land/MDRSGJ +landau/SM +landfall/MS +landfill/MS +landholder/SM +landholding/MS +landing/M +landlady/SM +landless/M +landline/MS +landlocked +landlord/MS +landlubber/MS +landmark/MS +landmass/MS +landmine/S +landowner/MS +landownership +landowning/SM +landscape/MZGDRS +landscaper/M +landslid +landslide/MGS +landslip/S +landsman/M +landsmen +landward/S +lane/MS +language/MS +languid/PY +languidness/M +languish/DSG +languor/SM +languorous/Y +lank/RYTP +lankiness/M +lankness/M +lanky/RTP +lanolin/M +lantern/MS +lanthanum/M +lanyard/MS +lap/SM +laparoscopic +laparoscopy +laparotomy +lapboard/SM +lapdog/SM +lapel/SM +lapidary/SM +lapin/SM +lapped +lappet/SM +lapping +lapse/AKGMSD +laptop/SM +lapwing/MS +larboard/SM +larcenist/SM +larcenous +larceny/SM +larch/MS +lard/MDRSZG +larder/M +lardy/RT +large/RSPMYT +largehearted +largeness/M +largess/M +largish +largo/SM +lariat/SM +lark/MDSG +larkspur/SM +larva/M +larvae +larval +laryngeal +larynges +laryngitis/M +larynx/M +lasagna/MS +lascivious/YP +lasciviousness/M +lase/ZGDRS +laser/M +lash/MDSGJ +lashing/M +lass/MS +lassie/SM +lassitude/M +lasso/SMDG +last/MDYSG +lasting/Y +lat/S +latch's +latch/UDSG +latchkey/SM +late/YTRP +latecomer/MS +latency/M +lateness/M +latent +lateral/MDYSG +latest/M +latex/M +lath/MDRSZG +lathe/M +lather/GMD +lathery +laths +latices +latish +latitude/MS +latitudinal +latitudinarian/MS +latrine/MS +latte/RSM +latter/MY +lattice/MDS +latticework/SM +laud/MDSGB +laudably +laudanum/M +laudatory +laugh/BMDG +laughably +laughing/MY +laughingstock/SM +laughs +laughter/M +launch/AGMDS +launcher/SM +launchpad/SM +launder/DRZGS +launderer/M +launderette/SM +laundress/MS +laundromat/MS +laundry/SM +laundryman/M +laundrymen +laundrywoman/M +laundrywomen +laureate/MS +laureateship/M +laurel/SM +lav/SGD +lava/M +lavage/M +lavaliere/SM +lavatorial +lavatory/SM +lave/S +lavender/SM +lavish/PTGDRSY +lavishness/M +law/SM +lawbreaker/SM +lawbreaking/M +lawful/UPY +lawfulness/UM +lawgiver/MS +lawless/PY +lawlessness/M +lawmaker/MS +lawmaking/M +lawman/M +lawmen +lawn/MS +lawnmower/SM +lawrencium/M +lawsuit/MS +lawyer/SM +lax/TRYP +laxative/MS +laxity/M +laxness/M +lay/AICSGM +layabout/S +layaway/M +layer/CSM +layered +layering/M +layette/MS +layman/M +laymen +layoff/SM +layout/SM +layover/MS +laypeople +layperson/MS +layup/SM +laywoman/M +laywomen +laze/MGDS +lazily +laziness/M +lazy/DRSTGP +lazybones/M +lb/S +lbw +lea/SM +leach/DSG +lead/MDNRSZG +leader/M +leaderless +leadership/SM +leading/M +leaf/MDSG +leafage/M +leafless +leaflet/GMDS +leafstalk/MS +leafy/RT +league/DSMG +leak/MDSG +leakage/MS +leakiness/M +leaky/PRT +lean/MDRSTGJP +leaning/M +leanness/M +leap/MDRSZG +leaper/M +leapfrog/MS +leapfrogged +leapfrogging +leapt +learn/AUGDS +learnability +learnable +learnedly +learner/MS +learning's +lease/ADSMG +leaseback/SM +leasehold/MRSZ +leaseholder/M +leaser/SM +leash's +leash/UDSG +least/M +leastwise +leather/MS +leatherette/M +leatherneck/MS +leathery +leave/DRSMZGJ +leaven/SGMD +leavened/U +leavening/M +leaver/M +leavings/M +lech/MDRSZG +lecher/M +lecherous/PY +lecherousness/M +lechery/M +lecithin/M +lectern/MS +lecture/MZGDRS +lecturer/M +lectureship/SM +ledge/RSMZ +ledger/M +lee/RSMZ +leech/MDSG +leek/MS +leer/MDG +leeriness/M +leery/RPT +leeward/SM +leeway/M +left/MRST +leftism/M +leftist/SM +leftmost +leftover/SM +leftward/S +lefty/SM +leg/SM +legacy/SM +legal/SMY +legalese/M +legalism/MS +legalistic +legalistically +legality/SM +legalization/M +legalize/GDS +legate/CXMNS +legatee/MS +legation's/AC +legato/SM +legend/SM +legendarily +legendary +legerdemain/M +legged +legginess/M +legging/MS +leggy/RPT +leghorn/MS +legibility/M +legible +legibly +legion/SM +legionary/SM +legionnaire/SM +legislate/DSGNV +legislation/M +legislative/Y +legislator/MS +legislature/SM +legit +legitimacy/M +legitimate/DSYG +legitimatize/GDS +legitimization/M +legitimize/DSG +legless +legman/M +legmen +legroom/SM +legume/MS +leguminous +legwarmer/S +legwork/M +lei/SM +leisure/DMY +leisureliness/M +leisurewear/M +leitmotif/MS +leitmotiv/MS +lemma/S +lemme/JG +lemming/M +lemon/SM +lemonade/SM +lemongrass +lemony +lemur/SM +lend/RSZG +lender/M +length/MNX +lengthen/GD +lengthily +lengthiness/M +lengths +lengthwise +lengthy/PRT +lenience/M +leniency/M +lenient/Y +lenitive +lens/MS +lent +lentil/MS +lento +leonine +leopard/SM +leopardess/MS +leotard/SM +leper/SM +leprechaun/MS +leprosy/M +leprous +lepta +lepton/MS +lesbian/SM +lesbianism/M +lesion/MS +less/MNRX +lessee/MS +lessen/GD +lesson/MS +lessor/MS +let/ISM +letdown/SM +lethal/Y +lethargic +lethargically +lethargy/M +letter/ZGMDRS +letterbomb/S +letterbox/S +lettered/U +letterer/M +letterhead/MS +lettering/M +letterpress/M +letting/S +lettuce/MS +letup/SM +leucine +leucotomy/S +leukemia/M +leukemic/SM +leukocyte/MS +levee/SM +level/PSZGMDRY +leveler/M +levelheaded/P +levelheadedness/M +levelness/M +lever/SGMD +leverage's +leverage/CDSG +leviathan/MS +levier/M +levitate/DSGN +levitation/M +levity/M +levy/DRSMZG +lewd/RYPT +lewdness/M +lexer/S +lexical +lexicographer/MS +lexicographic +lexicographical +lexicography/M +lexicon/SM +lexis +lg +liabilities +liability/AM +liable/A +liaise/GDS +liaison/MS +liar/MS +lib/M +libation/SM +libber/MS +libel/SZGMDR +libeler/M +libelous +liberal/MYPS +liberalism/M +liberality/M +liberalization/SM +liberalize/GDS +liberalness/M +liberate/CDSGN +liberation/CM +liberator/MS +libertarian/SM +libertine/MS +liberty/SM +libidinal +libidinous +libido/MS +librarian/MS +librarianship +library/SM +librettist/MS +libretto/SM +lice +license/MGDS +licensed/U +licensee/MS +licentiate/SM +licentious/YP +licentiousness/M +lichen/MS +licit/Y +lick/MDJSG +licking/M +licorice/SM +lid/SM +lidded +lidless +lido/MS +lie/DSM +lied/MR +lief/RT +liege/SM +lien/MS +lieu/M +lieutenancy/M +lieutenant/MS +life/MZR +lifebelt/S +lifeblood/M +lifeboat/MS +lifebuoy/MS +lifeforms +lifeguard/SM +lifeless/YP +lifelessness/M +lifelike +lifeline/MS +lifelong +lifer/M +lifesaver/SM +lifesaving/M +lifespan/S +lifestyle/SM +lifetime/MS +lifework/MS +lift/MDRSZG +lifter/M +liftoff/SM +ligament/MS +ligate/GNDS +ligation/M +ligature/MGDS +light's/C +light/CASTGD +lighted/U +lighten/SDRZG +lightener/M +lighter/SM +lightface/MD +lightheaded +lighthearted/YP +lightheartedness/M +lighthouse/MS +lighting's +lightly +lightness/M +lightning/MDS +lightproof +lightship/MS +lightweight/SM +ligneous +lignin +lignite/M +lii +likability/M +likable/P +likableness/M +like/EMGDST +likelihood/UM +likelihoods +likeliness/UM +likely/UPRT +liken/SGD +likeness/UM +likenesses +liker +likewise +liking/M +lilac/SM +lilliputian +lilo/S +lilt/MDSG +lily/SM +limb/MS +limber/UDSG +limberness/M +limbless +limbo/SM +lime/MGDS +limeade/SM +limelight/M +limerick/SM +limescale +limestone/M +limey/S +limit's +limit/CSZGDR +limitation/CM +limitations +limited/U +limiter's +limiting/S +limitless/P +limitlessness/M +limn/DSG +limo/MS +limousine/MS +limp/MDRYSPTG +limpet/MS +limpid/YP +limpidity/M +limpidness/M +limpness/M +limy/RT +linage/M +linchpin/SM +linden/MS +line/MZGDRSJ +lineage/MS +lineal/Y +lineament/SM +linear/Y +linearity/M +linebacker/MS +lined/U +linefeed +lineman/M +linemen +linen/SM +linens/M +liner/M +linesman/M +linesmen +lineup/MS +ling/M +linger/ZGJDRS +lingerer/M +lingerie/M +lingering/Y +lingo/M +lingoes +lingual +linguine/M +linguist/SM +linguistic/S +linguistically +linguistics/M +liniment/SM +lining/M +link/MDRSG +linkage/MS +linkman +linkmen +linkup/MS +linnet/MS +lino +linoleum/M +linseed/M +lint's +lint/CDG +lintel/MS +lints +linty/TR +lion/MS +lioness/MS +lionhearted +lionization/M +lionize/GDS +lip/SM +lipid/SM +liposuction/M +lipped +lippy +lipread/GRS +lipreader/M +lipreading/M +lipstick/MDSG +liq +liquefaction/M +liquefy/DSG +liqueur/SM +liquid/MS +liquidate/XGNDS +liquidation/M +liquidator/MS +liquidity/M +liquidize/ZGDRS +liquidizer/M +liquor/MDGS +lira/M +lire +lisle/M +lisp/MDRSZG +lisper/M +lissome +list/MDNSJXG +listed/U +listen/BMDRZG +listener/M +listeria +listing/M +listless/YP +listlessness/M +lit/ZR +litany/SM +litchi/MS +lite +liter/M +literacy/M +literal/SMYP +literalness/M +literariness/M +literary/P +literate/SMY +literati/M +literature/M +lithe/RPYT +litheness/M +lithesome +lithium/M +lithograph/MDRZG +lithographer/M +lithographic +lithographically +lithographs +lithography/M +lithosphere/SM +litigant/SM +litigate/DSGN +litigation/M +litigator/MS +litigious/P +litigiousness/M +litmus/M +litotes/M +litter/MDRSZG +litterateur/MS +litterbug/MS +litterer/M +little/MTRP +littleness/M +littoral/SM +liturgical/Y +liturgist/SM +liturgy/SM +livability/M +livable/U +live/ATGDSB +livelihood/SM +liveliness/M +livelong/S +lively/PRT +liven/SGD +liver's +liver/S +liveried +liverish +liverwort/MS +liverwurst/M +livery/CSM +liveryman/CM +liverymen/C +livestock/M +liveware +livid/Y +living/MS +lix/K +lizard/MS +ll +llama/SM +llano/SM +lo +load's +load/AUGSD +loadable +loader/MS +loading's +loaf/MDRSZG +loafer/M +loam/M +loamy/TR +loan/MDRSZG +loaner/M +loansharking/M +loanword/MS +loath/JZGDRS +loathe +loather/M +loathing/M +loathsome/PY +loathsomeness/M +loaves +lob/SMD +lobar +lobbed +lobber/MS +lobbing +lobby/GDSM +lobbyist/MS +lobe/MS +lobotomize/DSG +lobotomy/SM +lobster/MS +local/SMY +locale/MS +locality/SM +localization/M +localize/DSG +locate/EAGNDS +location's/A +location/ESM +locator/MS +locavore/SM +loci +lock/MDRSBZG +locker/M +locket/MS +lockjaw/M +lockout/MS +locksmith/M +locksmiths +lockstep/M +lockup/MS +loco/S +locomotion/M +locomotive/MS +locoweed/SM +locum/S +locus/M +locust/SM +locution/MS +lode/MS +lodestar/MS +lodestone/MS +lodge/DRSJMZG +lodger/M +lodging/M +lodgings/M +loft/MDSG +loftily +loftiness/M +lofty/PRT +log/SM +loganberry/SM +logarithm/SM +logarithmic +logbook/SM +loge/MS +logged +logger/SM +loggerhead/SM +loggia/SM +logging/M +logic/M +logical/Y +logicality/M +logician/MS +login/SM +logistic/S +logistical/Y +logistics/M +logjam/SM +logo/MS +logoff/SM +logon/SM +logotype/SM +logout/SM +logrolling/M +logy/RT +loin/MS +loincloth/M +loincloths +loiter/ZGSDR +loiterer/M +loitering/M +lolcat/SM +loll/DSG +lollipop/SM +lollop/GSD +lolly/S +lollygag/S +lollygagged +lollygagging +lone/YZR +loneliness/M +lonely/PTR +loner/M +lonesome/YP +lonesomeness/M +long's +long/KDSTG +longboat/MS +longbow/MS +longer +longevity/M +longhair/MS +longhand/M +longhorn/MS +longhouse/S +longing/MYS +longish +longitude/MS +longitudinal/Y +longshoreman/M +longshoremen +longsighted +longstanding +longtime +longueur/SM +longways +loo +loofah/M +loofahs +look/MDRSZG +lookalike/MS +looker/M +lookout/MS +lookup +loom/MDSG +loon/MS +loonie/M +loony/RSMT +loop/MDSG +loophole/MS +loopy/RT +loos/NRX +loose/UDSTG +loosely +loosen/UGSD +looseness/M +loot/MDRSZG +looter/M +looting/M +lop/S +lope/MGDS +lopped +lopping +lopsided/YP +lopsidedness/M +loquacious/PY +loquaciousness/M +loquacity/M +lord/MDYSG +lordliness/M +lordly/TPR +lordship/SM +lore/M +lorgnette/SM +loris/MS +lorn +lorry/SM +lose/ZGRSJ +loser/M +losing/M +loss/MS +lossless +lost +lot/SM +lotion/SM +lottery/SM +lotto/M +lotus/MS +louche +loud/RYTP +loudhailer/SM +loudmouth/MD +loudmouths +loudness/M +loudspeaker/MS +lough +loughs +lounge/MZGDRS +lounger/M +lour/DSG +louse's +louse/CDSG +lousily +lousiness/M +lousy/TPR +lout/MS +loutish/PY +louver/MDS +lovableness/M +lovably +love/MYZGDRSB +lovebird/SM +lovechild/M +loved/U +loveless +loveliness/M +lovelorn +lovely/RSMTP +lovemaking/M +lover/M +lovesick +lovey/S +loving/Y +low/SZTGMDRYP +lowborn +lowboy/MS +lowbrow/SM +lowdown/M +lower/GD +lowercase/M +lowermost +lowish +lowland/SZMR +lowlander/M +lowlife/SM +lowliness/M +lowly/TPR +lowness/M +lox/M +loyal/ETY +loyaler +loyalism/M +loyalist/SM +loyalties +loyalty/EM +lozenge/SM +ltd +luau/MS +lubber/MYS +lube/MGDS +lubricant/SM +lubricate/DSGN +lubrication/M +lubricator/MS +lubricious/Y +lubricity/M +lucid/PY +lucidity/M +lucidness/M +luck/MDSG +luckily/U +luckiness/UM +luckless +lucky/UPTR +lucrative/YP +lucrativeness/M +lucre/M +lucubrate/GNDS +lucubration/M +ludicrous/YP +ludicrousness/M +ludo +luff/DSG +lug/SM +luge/S +luggage/M +lugged +lugger/MS +lugging +lughole/S +lugsail/SM +lugubrious/YP +lugubriousness/M +lukewarm/YP +lukewarmness/M +lull/MDSG +lullaby/SM +lulu/S +lumbago/M +lumbar +lumber/MDRZGS +lumberer/M +lumbering/M +lumberjack/SM +lumberman/M +lumbermen +lumberyard/SM +lumen +luminary/SM +luminescence/M +luminescent +luminosity/M +luminous/Y +lummox/MS +lump/MDNSG +lumpectomy/S +lumpenproletariat +lumpiness/M +lumpish +lumpy/TRP +lunacy/SM +lunar +lunatic/SM +lunch/GMDS +lunchbox/S +luncheon/SM +luncheonette/SM +lunchroom/MS +lunchtime/MS +lung/MDSG +lunge/SM +lungfish/MS +lungful/S +lunkhead/MS +lupine/MS +lupus/M +lurch/GMDS +lure/MGDS +lurgy +lurid/PY +luridness/M +lurk/DRSZG +luscious/PY +lusciousness/M +lush/MRSYPT +lushness/M +lust/MDRSG +luster/M +lusterless +lustful/Y +lustily +lustiness/M +lustrous/Y +lusty/PTR +lutanist/SM +lute/MS +lutenist/SM +lutetium/M +lux +luxuriance/M +luxuriant/Y +luxuriate/DSGN +luxuriation/M +luxurious/PY +luxuriousness/M +luxury/SM +lvi +lvii +lxi +lxii +lxiv +lxix +lxvi +lxvii +lyceum/MS +lychgate/S +lye/MG +lying/M +lymph/M +lymphatic/SM +lymphocyte/SM +lymphoid +lymphoma/SM +lynch/JZGDRS +lyncher/M +lynching/M +lynx/MS +lyre/MS +lyrebird/MS +lyric/SM +lyrical/Y +lyricism/M +lyricist/SM +lysosomal +lysosomes +m/KAS +ma'am +ma/SMH +mac/SGMD +macabre +macadam/M +macadamia/SM +macadamize/GDS +macaque/MS +macaroni/MS +macaroon/MS +macaw/SM +mace/MS +macerate/DSGN +maceration/M +mach/M +machete/SM +machinate/GNDSX +machination/M +machine/DSMGB +machinery/M +machinist/MS +machismo/M +macho/M +mackerel/SM +mackinaw/SM +mackintosh/MS +macrame/M +macro/SM +macrobiotic/S +macrobiotics/M +macrocosm/SM +macroeconomic/S +macroeconomics/M +macrology/S +macron/MS +macrophages +macroscopic +mad/SMYP +madam/SM +madame/M +madcap/MS +madden/DGS +maddening/Y +madder/MS +maddest +madding +made/AU +mademoiselle/MS +madhouse/SM +madman/M +madmen +madness/M +madras/MS +madrasa/SM +madrasah/M +madrasahs +madrassa/SM +madrigal/SM +madwoman/M +madwomen +maelstrom/SM +maestro/SM +mafia/SM +mafiosi +mafioso/M +mag/SM +magazine/SM +mage/MS +magenta/M +maggot/MS +maggoty +magi/M +magic/SM +magical/Y +magician/SM +magicked +magicking +magisterial/Y +magistracy/M +magistrate/SM +magma/M +magnanimity/M +magnanimous/Y +magnate/SM +magnesia/M +magnesium/M +magnet/MS +magnetic +magnetically +magnetism/M +magnetite/M +magnetizable +magnetization/CM +magnetize/CGDS +magneto/SM +magnetometer/SM +magnetosphere +magnification/M +magnificence/M +magnificent/Y +magnifier/M +magnify/ZGXDRSN +magniloquence/M +magniloquent +magnitude/SM +magnolia/MS +magnon +magnum/MS +magpie/MS +magus/M +maharajah/M +maharajahs +maharani/SM +maharishi/SM +mahatma/SM +mahogany/SM +mahout/MS +maid/MNSX +maiden/MY +maidenhair/M +maidenhead/SM +maidenhood/M +maidservant/SM +mail/JMDRSZG +mailbag/SM +mailbomb/GSD +mailbox/MS +mailer/M +mailing/M +maillot/SM +mailman/M +mailmen +mailshot/S +maim/DSG +main/MYS +mainframe/SM +mainland/MS +mainline/MGDS +mainmast/MS +mainsail/MS +mainspring/MS +mainstay/MS +mainstream/SMDG +maintain/ZGBDRS +maintainability +maintainable/U +maintained/U +maintenance/M +maintop/SM +maisonette/MS +maize/SM +majestic +majestically +majesty/SM +majolica/M +major/SGMDY +majordomo/MS +majorette/MS +majoritarian/SM +majoritarianism +majority/SM +make's/A +make/UAGS +makeover/MS +maker/SM +makeshift/SM +makeup/MS +makeweight/S +making/MS +makings/M +malachite/M +maladjusted +maladjustment/M +maladministration +maladroit/PY +maladroitness/M +malady/SM +malaise/M +malamute/MS +malapropism/SM +malaria/M +malarial +malarkey/M +malathion/M +malcontent/MS +male/MPS +malediction/SM +malefaction/M +malefactor/SM +malefic +maleficence/M +maleficent +maleness/M +malevolence/M +malevolent/Y +malfeasance/M +malformation/SM +malformed +malfunction/MDSG +malice/M +malicious/PY +maliciousness/M +malign/DSG +malignancy/SM +malignant/Y +malignity/M +malinger/ZGSDR +malingerer/M +mall/MS +mallard/SM +malleability/M +malleable +mallet/MS +mallow/MS +malnourished +malnutrition/M +malocclusion/M +malodorous +malpractice/SM +malt/MDSG +malted/MS +maltose/M +maltreat/GLDS +maltreatment/M +malty/TR +malware/M +mam/S +mama/MS +mamba/SM +mambo/SGMD +mamma/M +mammal/MS +mammalian/MS +mammary +mammogram/MS +mammography/M +mammon/M +mammoth/M +mammoths +mammy/SM +man's/F +man/USY +manacle/DSMG +manage/ZGDRSL +manageability/M +manageable/U +management/MS +manager/M +manageress/S +managerial +manana/MS +manatee/SM +mandala/SM +mandamus/MS +mandarin/MS +mandate/DSMG +mandatory +mandible/MS +mandibular +mandolin/MS +mandrake/MS +mandrel/SM +mandrill/MS +mane/MDS +manege/M +maneuver/MDGSBJ +maneuverability/M +manful/Y +manga/M +manganese/M +mange/DRMZ +manger/M +mangetout/S +manginess/M +mangle/MZGDRS +mango/M +mangoes +mangrove/MS +mangy/TRP +manhandle/GDS +manhole/SM +manhood/M +manhunt/SM +mania/SM +maniac/MS +maniacal/Y +manic/SM +manically +manicure/MGDS +manicurist/MS +manifest/MDYSG +manifestation/SM +manifesto/SM +manifold/GMDS +manikin/SM +manila/M +manioc/MS +manipulable +manipulate/XGNVDS +manipulation/M +manipulative/Y +manipulator/MS +mankind/M +manky +manlike +manliness/M +manly/UTR +manna/M +manned/U +mannequin/SM +manner/MDYS +mannerism/SM +mannerly/U +manning/U +mannish/YP +mannishness/M +manometer/SM +manor/SM +manorial +manpower/M +manque +mansard/MS +manse/SXMN +manservant/M +mansion/M +manslaughter/M +manta/SM +mantel/MS +mantelpiece/SM +mantelshelf +mantelshelves +mantes +mantilla/SM +mantis/MS +mantissa/SM +mantle's +mantle/EGDS +mantra/MS +manual/MYS +manufacture/DRSMZG +manufacturer/M +manufacturing/M +manumission/SM +manumit/S +manumitted +manumitting +manure/MGDS +manuscript/MS +many/M +map's +map/AS +maple/SM +mapmaker/SM +mapped/A +mapper/MS +mapping/S +mar/S +marabou/MS +marabout/SM +maraca/MS +maraschino/MS +marathon/SMRZ +marathoner/M +maraud/ZGDRS +marauder/M +marble/MGDS +marbleize/GDS +marbling/M +march/ZGMDRS +marcher/M +marchioness/MS +mare/MS +margarine/M +margarita/MS +marge +margin/MS +marginal/YS +marginalia/M +marginalization/M +marginalize/GDS +maria/M +mariachi/MS +marigold/MS +marijuana/M +marimba/SM +marina/MS +marinade/DSMG +marinara/M +marinate/DSGN +marination/M +marine/MZRS +mariner/M +marionette/MS +marital/Y +maritime +marjoram/M +mark/AMDSG +markdown/SM +marked/U +markedly +marker/MS +market/MDRZGBS +marketability/M +marketable/U +marketeer/SM +marketer/M +marketing/M +marketplace/SM +marking/SM +markka/M +markkaa +marksman/M +marksmanship/M +marksmen +markup/MS +marl/M +marlin/MS +marlinespike/SM +marmalade/M +marmoreal +marmoset/SM +marmot/MS +maroon/MDGS +marque/MS +marquee/SM +marquess/MS +marquetry/M +marquis/MS +marquise/M +marquisette/M +marred/U +marriage/ASM +marriageability/M +marriageable +married/SM +marring +marrow/MS +marry/AGDS +marsh/MS +marshal/SMDG +marshland/SM +marshmallow/SM +marshy/RT +marsupial/MS +mart/MNSX +marten/M +martensite +martial/Y +martian/S +martin/MS +martinet/MS +martingale/MS +martini/SM +martyr/MDGS +martyrdom/M +marvel/MDGS +marvelous/Y +marzipan/M +masc +mascara/GMDS +mascot/MS +masculine/SM +masculinity/M +maser/SM +mash/MDRSZG +masher/M +mashup/MS +mask's +mask/UDSG +masker/MS +masochism/M +masochist/SM +masochistic +masochistically +mason/SM +masonic +masonry/M +masque/MS +masquerade/DRSMZG +masquerader/M +mass/MDSGV +massacre/MGDS +massage/DSMG +masseur/SM +masseuse/MS +massif/MS +massive/PY +massiveness/M +mast/MDS +mastectomy/SM +master's +master/ADGS +masterclass/S +masterful/Y +masterly +mastermind/SGMD +masterpiece/MS +masterstroke/SM +masterwork/MS +mastery/M +masthead/MS +mastic/M +masticate/GNDS +mastication/M +mastiff/SM +mastitis +mastodon/SM +mastoid/SM +masturbate/GNDS +masturbation/M +masturbatory +mat/SZGMDR +matador/SM +match/AMS +matchbook/SM +matchbox/MS +matched/U +matching +matchless +matchlock/SM +matchmaker/MS +matchmaking/M +matchstick/MS +matchwood/M +mate/MS +material/SMY +materialism/M +materialist/SM +materialistic +materialistically +materialization/M +materialize/DSG +materiel/M +maternal/Y +maternity/M +matey/S +mathematical/Y +mathematician/SM +mathematics/M +matinee/SM +mating/M +matins/M +matriarch/M +matriarchal +matriarchs +matriarchy/SM +matrices +matricidal +matricide/MS +matriculate/DSGN +matriculation/M +matrimonial +matrimony/M +matrix/M +matron/MYS +matte/DRSMZG +matter/MDG +matting/M +mattock/SM +mattress/MS +maturate/GNDS +maturation/M +mature/YTGDRS +maturity/SM +matzo/SMH +matzoh/M +matzohs +matzot +maudlin +maul/MDRSZG +mauler/M +maunder/SDG +mausoleum/SM +mauve/M +maven/SM +maverick/SM +maw/SM +mawkish/PY +mawkishness/M +max/GMDS +maxi/MS +maxilla/M +maxillae +maxillary +maxim/SM +maxima +maximal/Y +maximization/M +maximize/GDS +maximum/SM +may/M +maybe/SM +mayday/MS +mayflower/MS +mayfly/SM +mayhem/M +mayn't +mayo/M +mayonnaise/M +mayor/SM +mayoral +mayoralty/M +mayoress/MS +maypole/SM +mayst +maze/MS +mazurka/MS +mdse +me/DSH +mead/M +meadow/MS +meadowlark/MS +meager/PY +meagerness/M +meal/MS +mealiness/M +mealtime/SM +mealy/TPR +mealybug/SM +mealymouthed +mean/MRYJPSTG +meander/SMDJG +meanderings/M +meanie/M +meaning/M +meaningful/PY +meaningfulness/M +meaningless/YP +meaninglessness/M +meanness/M +meant/U +meantime/M +meanwhile/M +meany/SM +meas +measles/M +measly/RT +measurable +measurably +measure's +measure/ADSG +measured/U +measureless +measurement/MS +meat/MS +meatball/MS +meathead/MS +meatiness/M +meatless +meatloaf/M +meatloaves +meatpacking/M +meaty/TPR +mecca/SM +mechanic/MS +mechanical/Y +mechanics/M +mechanism/SM +mechanistic +mechanistically +mechanization/M +mechanize/DSG +medal/SM +medalist/MS +medallion/SM +meddle/ZGDRS +meddler/M +meddlesome +media/SM +medial/AY +median/MS +mediate/ADSGN +mediated/U +mediation/AM +mediator/MS +medic/SM +medicaid/M +medical/SMY +medicament/M +medicare/M +medicate/GNXDS +medication/M +medicinal/Y +medicine/MS +medico/MS +medieval +medievalist/MS +mediocre +mediocrity/SM +meditate/DSGNVX +meditation/M +meditative/Y +medium/MS +medley/MS +medulla/SM +medusa +medusae +meed/M +meek/RYPT +meekness/M +meerschaum/SM +meet/MJSG +meeting/M +meetinghouse/SM +meetup/MS +meg/S +mega +megabit/SM +megabucks/M +megabyte/MS +megachurch/MS +megacycle/SM +megadeath/M +megadeaths +megagram/S +megahertz/M +megajoule/S +megalith/M +megalithic +megaliths +megalomania/M +megalomaniac/SM +megalopolis/MS +megameter/S +megapascal/S +megaphone/DSMG +megapixel/SM +megastar/S +megaton/SM +megawatt/MS +meh +meiosis/M +meiotic +melamine/M +melancholia/M +melancholic/S +melancholy/M +melange/MS +melanin/M +melanoma/SM +meld/MDSG +melee/SM +meliorate/GNVDS +melioration/M +mellifluous/PY +mellifluousness/M +mellow/PTGDRYS +mellowness/M +melodic +melodically +melodious/YP +melodiousness/M +melodrama/MS +melodramatic/S +melodramatically +melodramatics/M +melody/SM +melon/SM +melt's +melt/ADSG +meltdown/SM +member's +member/EAS +membership/SM +membrane/SM +membranous +meme/MS +memento/MS +memo/MS +memoir/MS +memorabilia/M +memorability/M +memorable/U +memorably +memorandum/MS +memorial/SM +memorialize/DSG +memorization/M +memorize/DSG +memory/SM +memsahib/S +men/M +menace/MGDS +menacing/Y +menage/MS +menagerie/MS +mend/MDRSZG +mendacious/Y +mendacity/M +mendelevium/M +mender/M +mendicancy/M +mendicant/SM +mending/M +menfolk/MS +menfolks/M +menhaden/M +menial/MYS +meningeal +meninges +meningitis/M +meninx/M +menisci +meniscus/M +menopausal +menopause/M +menorah/M +menorahs +mensch/MS +menservants +menses/M +menstrual +menstruate/GNDS +menstruation/M +mensurable +mensuration/M +menswear/M +mental/Y +mentalist/SM +mentality/SM +menthol/M +mentholated +mention/GSMD +mentioned/U +mentor/MDSG +mentorship +menu/MS +meow/MDSG +mercantile +mercantilism/M +mercenary/SM +mercer/MS +mercerize/GDS +merchandise/MZGDRS +merchandiser/M +merchandising/M +merchant/MBS +merchantman/M +merchantmen +merciful/UY +merciless/PY +mercilessness/M +mercurial/Y +mercuric +mercury/M +mercy/SM +mere/MYTS +meretricious/YP +meretriciousness/M +merganser/MS +merge/DRSZG +merger/M +meridian/MS +meringue/MS +merino/MS +merit/CSM +merited/U +meriting +meritless +meritocracy/SM +meritocratic +meritorious/PY +meritoriousness/M +mermaid/SM +merman/M +mermen +merrily +merriment/M +merriness/M +merry/TRP +merrymaker/MS +merrymaking/M +mesa/MS +mescal/MS +mescalin +mescaline/M +mesdames +mesdemoiselles +mesh/MDSG +mesmeric +mesmerism/M +mesmerize/ZGDRS +mesmerizer/M +mesomorph/M +mesomorphs +meson/SM +mesosphere/SM +mesquite/SM +mess/MDSG +message/MGDS +messeigneurs +messenger/SM +messiah/M +messiahs +messianic +messieurs +messily +messiness/M +messmate/SM +messy/PTR +mestizo/MS +met +meta +metabolic +metabolically +metabolism/SM +metabolite/SM +metabolize/DSG +metacarpal/SM +metacarpi +metacarpus/M +metadata +metal/SMD +metalanguage/MS +metallic +metallurgic +metallurgical +metallurgist/MS +metallurgy/M +metalwork/MRZG +metalworker/M +metalworking/M +metamorphic +metamorphism/M +metamorphose/GDS +metamorphosis/M +metaphor/MS +metaphoric +metaphorical/Y +metaphysical/Y +metaphysics/M +metastases +metastasis/M +metastasize/DSG +metastatic +metatarsal/MS +metatarsi +metatarsus/M +metatheses +metathesis/M +mete/MZGDRS +metempsychoses +metempsychosis/M +meteor/MS +meteoric +meteorically +meteorite/SM +meteoroid/SM +meteorologic +meteorological +meteorologist/SM +meteorology/M +meter/GMD +metformin +methadone/M +methamphetamine/M +methane/M +methanol/M +methinks +method/MS +methodical/YP +methodicalness/M +methodological/Y +methodology/SM +methotrexate +methought +meths +methyl/M +meticulous/YP +meticulousness/M +metier/MS +metric/S +metrical/Y +metricate/GNDS +metrication/M +metricize/GDS +metro/SM +metronome/MS +metropolis/MS +metropolitan +mettle/M +mettlesome +mew/SGMD +mewl/DSG +mews/M +mezzanine/MS +mezzo/SM +mfg +mfr/S +mg +mgr +mi/MNX +miasma/MS +mic/S +mica/M +mice +mick/S +mickey/MS +micro/SM +microaggression/SM +microbe/MS +microbial +microbiological +microbiologist/MS +microbiology/M +microbrewery/SM +microchip/MS +microcircuit/SM +microcode +microcomputer/MS +microcosm/MS +microcosmic +microdot/SM +microeconomics/M +microelectronic/S +microelectronics/M +microfiber/MS +microfiche/M +microfilm/GMDS +microfinance +microfloppies +microgroove/SM +microlight/MS +microloan/MS +micromanage/ZGDRSL +micromanagement/M +micromanager/M +micrometeorite/SM +micrometer/MS +micron/MS +microorganism/MS +microphone/SM +microplastics +microprocessor/MS +microscope/SM +microscopic +microscopical/Y +microscopy/M +microsecond/MS +microsurgery/M +microwave/DSMGB +microwaveable +mid +midair/M +midday/M +midden/MS +middle/MGS +middlebrow/SM +middleman/M +middlemen +middlemost +middleweight/MS +middy/SM +midfield/RZ +midge/SM +midget/MS +midi/MS +midland/MS +midlife/M +midmost +midnight/M +midpoint/MS +midrib/MS +midriff/MS +midsection/MS +midshipman/M +midshipmen +midships +midsize +midst/M +midstream/M +midsummer/M +midterm/MS +midtown/M +midway/MS +midweek/MS +midwife/MGDS +midwifery/SM +midwinter/M +midwives +midyear/MS +mien/M +miff/DSG +might've +might/M +mightily +mightiness/M +mightn't +mighty/TRP +mignonette/SM +migraine/MS +migrant/MS +migrate/AGDS +migration/SM +migratory +mikado/MS +mike/MGDS +mil/SZMR +milady/SM +milch +mild/MRYTP +mildew/SMDG +mildness/M +mile/MS +mileage/SM +milepost/MS +miler/M +milestone/MS +milf/MS +milieu/SM +militancy/M +militant/MYS +militarily +militarism/M +militarist/SM +militaristic +militarization/CM +militarize/CDSG +military/M +militate/GDS +militia/SM +militiaman/M +militiamen +milk/MDRSZG +milker/M +milkiness/M +milkmaid/MS +milkman/M +milkmen +milkshake/SM +milksop/MS +milkweed/SM +milky/RTP +mill/MDRSZGJ +millage/M +millennia +millennial/M +millennium/MS +miller/M +millet/M +milliard/MS +millibar/MS +milligram/MS +milliliter/MS +millimeter/MS +milliner/MS +millinery/M +milling/M +million/HSM +millionaire/SM +millionairess/S +millionth/M +millionths +millipede/SM +millisecond/SM +millpond/SM +millrace/SM +millstone/SM +millstream/MS +millwright/SM +milometer/S +milquetoast/SM +milt/MDSG +mime/MGDS +mimeograph/GMD +mimeographs +mimetic +mimic/SM +mimicked +mimicker/SM +mimicking +mimicry/SM +mimosa/SM +min +minaret/MS +minatory +mince/DRSMZG +mincemeat/M +mincer/M +mind's +mind/ADRSZG +mindbogglingly +minded/P +mindful/YP +mindfulness/M +mindless/YP +mindlessness/M +mindset/MS +mine/MZGNDRSX +minefield/SM +miner/M +mineral/MS +mineralogical +mineralogist/MS +mineralogy/M +minestrone/M +minesweeper/SM +mingle/DSG +mingy +mini/MS +miniature/MS +miniaturist/MS +miniaturization/M +miniaturize/GDS +minibar/S +minibike/SM +minibus/MS +minicab/S +minicam/MS +minicomputer/SM +minifloppies +minim/SM +minima +minimal/Y +minimalism/M +minimalist/MS +minimization/M +minimize/DSG +minimum/MS +mining/M +minion/M +miniseries/M +miniskirt/MS +minister/SGMD +ministerial +ministrant/MS +ministration/MS +ministry/SM +minivan/MS +mink/MS +minnesinger/MS +minnow/SM +minor/SMDG +minority/SM +minoxidil/M +minster/MS +minstrel/SM +minstrelsy/M +mint/MDRSZG +mintage/M +minter/M +minty/RT +minuend/MS +minuet/SM +minus/MS +minuscule/MS +minute/PDRSMYTG +minuteman/M +minutemen +minuteness/M +minutia/M +minutiae +minx/MS +miracle/MS +miraculous/Y +mirage/SM +mire/MGDS +mirror/GSMD +mirth/M +mirthful/PY +mirthfulness/M +mirthless/Y +miry/RT +misaddress/DSG +misadventure/MS +misaligned +misalignment/M +misalliance/MS +misanthrope/SM +misanthropic +misanthropically +misanthropist/MS +misanthropy/M +misapplication/M +misapply/DSGNX +misapprehend/GSD +misapprehension/MS +misappropriate/XDSGN +misappropriation/M +misbegotten +misbehave/GDS +misbehavior/M +misc +miscalculate/DSXGN +miscalculation/M +miscall/DSG +miscarriage/MS +miscarry/GDS +miscast/SG +miscegenation/M +miscellaneous/Y +miscellany/SM +mischance/SM +mischief/M +mischievous/YP +mischievousness/M +miscibility/M +miscible +misclassified +miscommunication/S +misconceive/GDS +misconception/SM +misconduct/MDGS +misconstruction/MS +misconstrue/GDS +miscount/MDSG +miscreant/SM +miscue/DSMG +misdeal/GMS +misdealt +misdeed/MS +misdemeanor/MS +misdiagnose/GDS +misdiagnosis/M +misdid +misdirect/SDG +misdirection/M +misdo/JG +misdoes +misdoing/M +misdone +miser/SBMY +miserableness/M +miserably +miserliness/M +misery/SM +misfeasance/M +misfeature/S +misfile/GDS +misfire/MGDS +misfit/SM +misfitted +misfitting +misfortune/SM +misgiving/MS +misgovern/SDGL +misgovernment/M +misguidance/M +misguide/DSG +misguided/Y +mishandle/DSG +mishap/SM +mishear/GS +misheard +mishit/S +mishitting +mishmash/MS +misidentify/GDS +misinform/DGS +misinformation/M +misinterpret/SGD +misinterpretation/SM +misjudge/DSG +misjudgment/SM +mislabel/GSD +mislaid +mislay/GS +mislead/GS +misleading/Y +misled +mismanage/LGDS +mismanagement/M +mismatch/GMDS +misname/GDS +misnomer/MS +misogamist/MS +misogamy/M +misogynist/SM +misogynistic +misogynous +misogyny/M +misplace/GLDS +misplacement/M +misplay/GMDS +misprint/GMDS +misprision/M +mispronounce/DSG +mispronunciation/SM +misquotation/MS +misquote/MGDS +misread/GJS +misreading/M +misremember/GDS +misreport/MDGS +misrepresent/GDS +misrepresentation/MS +misrule/MGDS +miss's +miss/EDSGV +missal/ESM +missed/U +misshape/GDS +misshapen +missile/MS +missilery/M +mission/AMS +missionary/SM +missioner/SM +missive/MS +misspeak/GS +misspell/GDJS +misspelling/M +misspend/GS +misspent +misspoke +misspoken +misstate/GDSL +misstatement/SM +misstep/MS +missus/MS +mist's +mist/CDRSZG +mistakable/U +mistake/BMGS +mistaken/Y +mister's +mistily +mistime/GDS +mistiness/M +mistletoe/M +mistook +mistral/MS +mistranslated +mistreat/LDGS +mistreatment/M +mistress/MS +mistrial/MS +mistrust/MDSG +mistrustful/Y +misty/PRT +mistype/GS +misunderstand/SGJ +misunderstanding/M +misunderstood +misuse/DSMG +mite/MZRS +miter/MDG +mitigate/DSGN +mitigated/U +mitigation/M +mitochondria +mitochondrial +mitochondrion +mitoses +mitosis/M +mitotic +mitral +mitt/MNSX +mitten/M +mitzvah +mix/ZGMDRSB +mixed/U +mixer/M +mixture/SM +mizzen/MS +mizzenmast/SM +mkay +mks +ml +mm +mnemonic/MS +mnemonically +mo/CKHS +moan/MDRSZG +moaner/M +moat/MDS +mob's +mob/CS +mobbed/C +mobbing/C +mobile/MS +mobility/M +mobilization/CM +mobilizations +mobilize/CDSG +mobilizer/SM +mobster/SM +moccasin/SM +mocha/SM +mock/DRSZG +mocker/M +mockery/SM +mocking/Y +mockingbird/SM +mod/STM +modal/SM +modality/S +modded +modding +mode/MS +model/ZGSJMDR +modeler/M +modeling/M +modem/SM +moderate/MYGNPDS +moderateness/M +moderation/M +moderator/SM +modern/MYPS +modernism/M +modernist/SM +modernistic +modernity/M +modernization/M +modernize/DRSZG +modernizer/M +modernness/M +modest/Y +modesty/M +modicum/SM +modifiable +modification/M +modified/U +modifier/M +modify/DRSXZGN +modish/YP +modishness/M +modular +modularization +modulate/CGNDS +modulation/CM +modulations +modulator/MS +module/MS +modulo +modulus +moggy +mogul/SM +mohair/M +moi +moiety/SM +moil/MDSG +moire/SM +moist/XTPNRY +moisten/DRZG +moistener/M +moistness/M +moisture/M +moisturize/ZGDRS +moisturizer/M +molar/SM +molasses/M +mold/MDRJSZG +moldboard/SM +molder/GMD +moldiness/M +molding/M +moldy/TPR +mole/MS +molecular +molecularity/M +molecule/SM +molehill/SM +moleskin/M +molest/DRZGS +molestation/M +molested/U +molester/M +moll/MS +mollification/M +mollify/DSNG +molluscan +mollusk/SM +molly/SM +mollycoddle/DSMG +molt/MDNRSZG +molter/M +molybdenum/M +mom/SM +moment/MS +momenta +momentarily +momentariness/M +momentary/P +momentous/PY +momentousness/M +momentum/M +mommy/SM +monad +monarch/M +monarchic +monarchical +monarchism/M +monarchist/MS +monarchistic +monarchs +monarchy/SM +monastery/SM +monastic/MS +monastical/Y +monasticism/M +monaural +monetarily +monetarism/M +monetarist/MS +monetary +monetization/C +monetize/CGDS +money/SMD +moneybag/MS +moneybox/S +moneylender/SM +moneymaker/SM +moneymaking/M +monger/MDGS +mongol/S +mongolism/M +mongoloid/MS +mongoose/MS +mongrel/SM +monies +moniker/SM +monism/M +monist/MS +monition/SM +monitor/SMDG +monitory +monk/MS +monkey/MDGS +monkeyshine/SM +monkish +monkshood/SM +mono/M +monochromatic +monochrome/MS +monocle/DSM +monoclonal +monocotyledon/SM +monocotyledonous +monocular +monodic +monodist/SM +monody/SM +monogamist/MS +monogamous/Y +monogamy/M +monogram/SM +monogrammed +monogramming +monograph/M +monographs +monolingual/MS +monolith/M +monolithic +monoliths +monologist/SM +monologue/SM +monomania/M +monomaniac/MS +monomaniacal +monomer/SM +mononucleosis/M +monophonic +monoplane/SM +monopolist/SM +monopolistic +monopolization/M +monopolize/DRSZG +monopolizer/M +monopoly/SM +monorail/MS +monosyllabic +monosyllable/MS +monotheism/M +monotheist/SM +monotheistic +monotone/MS +monotonic +monotonically +monotonous/PY +monotonousness/M +monotony/M +monounsaturated +monoxide/MS +monseigneur/M +monsieur/M +monsignor/SM +monsoon/SM +monsoonal +monster/SM +monstrance/ASM +monstrosity/SM +monstrous/Y +montage/SM +month/MY +monthly/SM +months +monument/MS +monumental/Y +moo/SGMD +mooch/ZGMDRS +moocher/M +mood/MS +moodily +moodiness/M +moody/TPR +moon/MDSG +moonbeam/MS +moonless +moonlight/SMDRZG +moonlighter/M +moonlighting/M +moonlit +moonscape/SM +moonshine/MZRS +moonshiner/M +moonshot/MS +moonstone/MS +moonstruck +moonwalk/MS +moor/MDJSG +moorhen/S +mooring/M +moorland/MS +moose/M +moot/DSG +mop/SZGMDR +mope/MS +moped/SM +moper/M +mopey +mopier +mopiest +mopish +mopped +moppet/MS +mopping +moraine/SM +moral/SMY +morale/M +moralism +moralist/MS +moralistic +moralistically +moralities +morality/UM +moralization/CM +moralize/CGDS +moralizer/MS +morass/MS +moratorium/SM +moray/SM +morbid/YP +morbidity/M +morbidness/M +mordancy/M +mordant/SMY +more/MS +moreish +morel/SM +moreover +mores/M +morgue/MS +moribund +morn/MJSG +morning/M +morocco/M +moron/SM +moronic +moronically +morose/YP +moroseness/M +morph/GD +morpheme/MS +morphemic +morphia/M +morphine/M +morphing/M +morphological +morphology/M +morphs +morrow/MS +morsel/MS +mortal/MYS +mortality/M +mortar/MDSG +mortarboard/SM +mortgage's +mortgage/AGDS +mortgagee/MS +mortgagor/MS +mortician/MS +mortification/M +mortify/NGDS +mortise/DSMG +mortuary/SM +mosaic/MS +mosey/SGD +mosh/DSG +mosque/MS +mosquito/M +mosquitoes +moss/MS +mossback/SM +mossy/TR +most/MY +mot/SM +mote's +mote/KCXSVN +motel/SM +motet/SM +moth/M +mothball/GMDS +mother/MDYSG +motherboard/SM +motherfucker/MS! +motherfucking/! +motherhood/M +motherland/MS +motherless +motherliness/M +moths +motif/SM +motile/S +motility/M +motion/KCM +motioned +motioning +motionless/YP +motionlessness/M +motivate/CDSG +motivated/U +motivation/SM +motivational +motivator/SM +motive/MS +motiveless +motley/MS +motlier +motliest +motocross/MS +motor/SGMD +motorbike/MGDS +motorboat/MS +motorcade/MS +motorcar/SM +motorcycle/DSMG +motorcyclist/MS +motorist/SM +motorization/M +motorize/DSG +motorman/M +motormen +motormouth/M +motormouths +motorway/SM +mottle/GDS +motto/M +mottoes +moue/MS +mound/SGMD +mount/EASGMD +mountable +mountain/SM +mountaineer/SMDG +mountaineering/M +mountainous +mountainside/SM +mountaintop/SM +mountebank/MS +mounted/U +mounter/MS +mounting/SM +mourn/SZGDR +mourned/U +mourner/M +mournful/YP +mournfulness/M +mourning/M +mouse/DRSMZG +mouser/M +mousetrap/SM +mousetrapped +mousetrapping +mousiness/M +moussaka/S +mousse/MGDS +mousy/PTR +mouth/GMD +mouthfeel +mouthful/MS +mouthiness/M +mouthpiece/MS +mouths +mouthwash/MS +mouthwatering +mouthy/PTR +mouton/M +movable/SM +move/AMZGDRSB +moved/U +movement/SM +mover/AM +movie/SM +moviegoer/SM +moving/Y +mow/SZGMDR +mower/M +moxie/M +mozzarella/M +mp +mpg +mph +mt +mtg +mtge +mu/SM +much/M +mucilage/M +mucilaginous +muck/MDSG +muckrake/DRSZG +muckraker/M +mucky/TR +mucous +mucus/M +mud/M +muddily +muddiness/M +muddle/MGDS +muddleheaded +muddy/PTGDRS +mudflap/S +mudflat/MS +mudguard/SM +mudpack/S +mudroom/MS +mudslide/MS +mudslinger/SM +mudslinging/M +muenster/M +muesli +muezzin/MS +muff/MDSG +muffin/MS +muffle/ZGDRS +muffler/M +mufti/SM +mug/SM +mugful/MS +mugged +mugger/MS +mugginess/M +mugging/MS +muggins +muggle/MS +muggy/PTR +mugshot/MS +mugwump/MS +mujaheddin +mukluk/MS +mulatto/M +mulattoes +mulberry/SM +mulch/GMDS +mulct/SGMD +mule/MS +muleskinner/MS +muleteer/MS +mulish/PY +mulishness/M +mull/DSG +mullah/M +mullahs +mullein/M +mullet/MS +mulligan/SM +mulligatawny/M +mullion/SMD +multi +multicellular +multichannel +multicolored +multicultural +multiculturalism/M +multidimensional +multidisciplinary +multifaceted +multifamily +multifarious/PY +multifariousness/M +multiform +multigrain +multilateral/Y +multilayered +multilevel +multilingual +multilingualism/M +multimedia/M +multimillionaire/SM +multinational/SM +multipart +multiparty +multiplayer/M +multiple/MS +multiplex/ZGMDRS +multiplexer/M +multiplicand/MS +multiplication/M +multiplicative +multiplicity/SM +multiplier/M +multiply/NZGDRSX +multiprocessing +multiprocessor/SM +multipurpose +multiracial +multistage +multistory +multitask/GS +multitasking/M +multitude/SM +multitudinous +multivariate +multiverse/SM +multivitamin/MS +multiyear +mum +mumble/MZGDRS +mumbler/M +mumbletypeg/M +mummer/MS +mummery/M +mummification/M +mummify/GNDS +mummy/SM +mumps/M +mun +munch/GDS +munchie/S +munchies/M +munchkin/SM +mundane/SY +mung/DSG +municipal/SMY +municipality/SM +munificence/M +munificent/Y +munition/MDGS +mural/SM +muralist/SM +murder/ZGMDRS +murderer/M +murderess/MS +murderous/Y +murk/MS +murkily +murkiness/M +murky/PTR +murmur/ZGJMDRS +murmurer/M +murmuring/M +murmurous +murrain/M +muscat/MS +muscatel/SM +muscle/MGDS +musclebound +muscleman +musclemen +muscly +muscular/Y +muscularity/M +musculature/M +musculoskeletal +muse/MGDSJ +musette/MS +museum/MS +mush/MDRSZG +mushiness/M +mushroom/GSMD +mushy/PTR +music/SM +musical/MYS +musicale/MS +musicality/M +musician/SMY +musicianship/M +musicological +musicologist/MS +musicology/M +musing/MY +musk/M +muskeg/MS +muskellunge/MS +musket/MS +musketeer/MS +musketry/M +muskie/M +muskiness/M +muskmelon/SM +muskox/MN +muskrat/MS +musky/PTRS +muslin/M +muss/MDSG +mussel/MS +mussy/TR +must've +must/MRSZ +mustache/MDS +mustachio/SMD +mustang/MS +mustard/M +muster/GMD +mustily +mustiness/M +mustn't +musty/PTR +mutability/M +mutably +mutagen/MS +mutagenic +mutant/MS +mutate/XGNVDS +mutation/M +mutational +mute/MYTGDRSPB +muteness/M +mutilate/DSGNX +mutilation/M +mutilator/SM +mutineer/SM +mutinous/Y +mutiny/GDSM +mutt/MS +mutter/ZGJMDRS +mutterer/M +muttering/M +mutton/M +muttonchops/M +muttony +mutual/Y +mutuality/M +muumuu/MS +muzak +muzzily +muzzle/DSMG +muzzy/P +my +mycologist/SM +mycology/M +myelitis/M +myna/MS +myocardial +myocardium +myopia/M +myopic +myopically +myriad/SM +myrmidon/MS +myrrh/M +myrtle/SM +mys +myself +mysterious/PY +mysteriousness/M +mystery/SM +mystic/SM +mystical/Y +mysticism/M +mystification/CM +mystify/CDSGN +mystique/M +myth/M +mythic +mythical +mythological +mythologist/SM +mythologize/DSG +mythology/SM +myths +myxomatosis +n/IKTH +naan/S +nab/S +nabbed +nabbing +nabob/SM +nacelle/SM +nacho/SM +nacre/M +nacreous +nadir/SM +nae +naff/RT +nag/SM +nagged +nagger/MS +nagging +nagware +nah +naiad/SM +naif/MS +nail/MDSG +nailbrush/MS +naive/RYT +naivete/M +naivety/M +naked/PY +nakedness/M +name's +name/AGDS +nameable/U +named/U +nameless/Y +namely +nameplate/MS +namesake/SM +nanny/SM +nanobot/S +nanometer/S +nanosecond/SM +nanotechnology/SM +nanotube +nap/SM +napalm/MDSG +nape/MS +naphtha/M +naphthalene/M +napkin/MS +napless +napoleon/SM +napped +napper/MS +napping +nappy/TRSM +narc/MS +narcissism/M +narcissist/MS +narcissistic +narcissus/M +narcolepsy/M +narcoleptic +narcoses +narcosis/M +narcotic/SM +narcotization/M +narcotize/GDS +nark +narky +narrate/GNVDSX +narration/M +narrative/SM +narrator/SM +narrow/PTGMDRYS +narrowness/M +narwhal/MS +nary +nasal/SMY +nasality/M +nasalization/M +nasalize/DSG +nascence/AM +nascent/A +nastily +nastiness/M +nasturtium/SM +nasty/PTR +natal +natch +nation/MS +national/MYS +nationalism/M +nationalist/SM +nationalistic +nationalistically +nationality/SM +nationalization/MS +nationalize/CDSG +nationhood/M +nationwide +native/MS +nativity/SM +natl +natter/GMDS +nattily +nattiness/M +natty/PTR +natural's +natural/UPY +naturalism/M +naturalist/SM +naturalistic +naturalization/M +naturalize/DSG +naturalness/UM +naturals +nature's +nature/CS +naturism +naturist/S +naught/MS +naughtily +naughtiness/M +naughty/PTR +nausea/M +nauseam +nauseate/GDS +nauseating/Y +nauseous/PY +nauseousness/M +nautical/Y +nautilus/MS +naval +nave/MS +navel/SM +navigability/M +navigable +navigate/DSGN +navigation/M +navigational +navigator/MS +navvy/S +navy/SM +nay/SM +naysayer/MS +ne'er +neanderthal/MS +neap/MS +near/DRYSPTG +nearby +nearness/M +nearshore +nearside +nearsighted/YP +nearsightedness/M +neat/NRYPXT +neaten/GD +neath +neatness/M +nebula/M +nebulae +nebular +nebulous/PY +nebulousness/M +necessarily/U +necessary/SM +necessitate/DSG +necessitous +necessity/SM +neck/MDSG +neckband/S +neckerchief/MS +necking/M +necklace/MGDSJ +neckline/MS +necktie/MS +necrology/M +necromancer/SM +necromancy/M +necrophilia +necrophiliac/S +necropolis/MS +necroses +necrosis/M +necrotic +nectar/M +nectarine/MS +nee +need/MDSG +needed/U +needful/Y +neediness/M +needle/MGDS +needlepoint/M +needless/YP +needlessness/M +needlewoman/M +needlewomen +needlework/M +needn't +needy/PTR +nefarious/YP +nefariousness/M +neg +negate/DSGNVX +negation/M +negative/MYGPDS +negativeness/M +negativism/M +negativity/M +neglect/SGMD +neglectful/YP +neglectfulness/M +negligee/MS +negligence/M +negligent/Y +negligible +negligibly +negotiability/M +negotiable/A +negotiate/ADSGN +negotiation/AM +negotiations +negotiator/MS +negritude/M +negro +negroid +neigh/MDG +neighbor/SMDYG +neighborhood/SM +neighborliness/M +neighs +neither +nelson/SM +nematode/SM +nemeses +nemesis/M +neoclassic +neoclassical +neoclassicism/M +neocolonialism/M +neocolonialist/MS +neocon/SM +neoconservative/SM +neocortex +neodymium/M +neolithic +neologism/SM +neon/M +neonatal +neonate/MS +neophilia +neophyte/MS +neoplasm/MS +neoplastic +neoprene/M +nepenthe/M +nephew/SM +nephrite/M +nephritic +nephritis/M +nephropathy +nepotism/M +nepotist/SM +nepotistic +neptunium/M +nerd/MS +nerdy/RT +nerve's +nerve/UDSG +nerveless/YP +nervelessness/M +nerviness/M +nervous/YP +nervousness/M +nervy/TPR +nest/MDSG +nestle/GJDS +nestling/M +net/SM +netball +netbook/MS +nether +nethermost +netherworld/M +netiquette/S +netted +netter/S +netting/M +nettle/MGDS +nettlesome +network/SGMD +networking/M +neural/Y +neuralgia/M +neuralgic +neurasthenia/M +neurasthenic/MS +neuritic/MS +neuritis/M +neurological/Y +neurologist/SM +neurology/M +neuron/MS +neuronal +neuroscience +neuroses +neurosis/M +neurosurgeon/MS +neurosurgery/M +neurosurgical +neurotic/MS +neurotically +neuroticism +neurotransmitter/SM +neut +neuter/MDGS +neutral/SMY +neutralism/M +neutralist/SM +neutrality/M +neutralization/M +neutralize/DRSZG +neutralizer/M +neutrino/SM +neutron/SM +never +nevermore +nevertheless +nevi +nevus/M +new/STMRYP +newbie/MS +newborn/SM +newcomer/SM +newel/SM +newfangled +newfound +newline/S +newlywed/SM +newness/M +news/M +newsagent/S +newsboy/SM +newscast/SMRZ +newscaster/M +newsdealer/SM +newsflash/S +newsgirl/SM +newsgroup/MS +newshound/S +newsletter/MS +newsman/M +newsmen +newspaper/MS +newspaperman/M +newspapermen +newspaperwoman/M +newspaperwomen +newspeak +newsprint/M +newsreader/S +newsreel/MS +newsroom/MS +newsstand/SM +newsweekly/SM +newswoman/M +newswomen +newsworthiness/M +newsworthy/P +newsy/TR +newt/MS +newton/MS +next/M +nexus/MS +niacin/M +nib/SM +nibble/MZGDRS +nibbler/M +nice/PYTR +niceness/M +nicety/SM +niche/SM +nick/MDRSZG +nickel/MS +nickelodeon/SM +nicker/MDG +nickle/S +nickname/DSMG +nicotine/M +niece/SM +nifedipine +niff +niffy +nifty/TR +nigga/MS! +niggard/SMY +niggardliness/M +niggaz/! +nigger/SM! +niggle/MZGDRS +niggler/M +nigh/RT +night/SMY +nightcap/SM +nightclothes/M +nightclub/SM +nightclubbed +nightclubbing +nightdress/MS +nightfall/M +nightgown/SM +nighthawk/SM +nightie/SM +nightingale/SM +nightlife/M +nightlight/S +nightlong +nightmare/SM +nightmarish +nightshade/SM +nightshirt/SM +nightspot/MS +nightstand/SM +nightstick/SM +nighttime/M +nightwatchman +nightwatchmen +nightwear/M +nihilism/M +nihilist/MS +nihilistic +nil/M +nimbi +nimble/TPR +nimbleness/M +nimbly +nimbus/M +nimby +nimrod/MS +nincompoop/SM +nine/MS +ninepin/MS +ninepins/M +nineteen/SMH +nineteenth/M +nineteenths +ninetieth/M +ninetieths +ninety/HSM +ninja/SM +ninny/SM +ninth/M +ninths +niobium/M +nip/SM +nipped +nipper/MS +nippiness/M +nipping +nipple/MS +nippy/TPR +nirvana/M +nisei/M +nit/SMR +niter/M +nitpick/SZGDR +nitpicker/M +nitpicking/M +nitrate/DSMGN +nitration/M +nitric +nitrification/M +nitrite/SM +nitro +nitrocellulose/M +nitrogen/M +nitrogenous +nitroglycerin/M +nitwit/MS +nix/GMDS +no/SM +nob/SY +nobble/GDS +nobelium/M +nobility/M +noble/RSPMT +nobleman/M +noblemen +nobleness/M +noblewoman/M +noblewomen +nobody/SM +nocturnal/Y +nocturne/MS +nod/SM +nodal +nodded +nodding +noddle/MS +noddy +node/MS +nodular +nodule/MS +noel/MS +noes +noggin/MS +nohow +noise/DSMG +noiseless/PY +noiselessness/M +noisemaker/MS +noisily +noisiness/M +noisome +noisy/PTR +nomad/SM +nomadic +nomenclature/MS +nominal/Y +nominate/ACGNVDS +nomination's/A +nomination/CSM +nominative/SM +nominator/CSM +nominee/MS +non +nonabrasive +nonabsorbent/SM +nonacademic +nonacceptance/M +nonacid +nonactive/MS +nonaddictive +nonadhesive +nonadjacent +nonadjustable +nonadministrative +nonage/MS +nonagenarian/MS +nonaggression/M +nonalcoholic +nonaligned +nonalignment/M +nonallergic +nonappearance/MS +nonassignable +nonathletic +nonattendance/M +nonautomotive +nonavailability/M +nonbasic +nonbeliever/MS +nonbelligerent/MS +nonbinding +nonbreakable +nonburnable +noncaloric +noncancerous +nonce/M +nonchalance/M +nonchalant/Y +nonchargeable +nonclerical/MS +nonclinical +noncollectable +noncom/MS +noncombat +noncombatant/MS +noncombustible +noncommercial/MS +noncommittal/Y +noncommunicable +noncompeting +noncompetitive +noncompliance/M +noncomplying +noncomprehending +nonconducting +nonconductor/MS +nonconforming +nonconformism +nonconformist/MS +nonconformity/M +nonconsecutive +nonconstructive +noncontagious +noncontinuous +noncontributing +noncontributory +noncontroversial +nonconvertible +noncooperation/M +noncorroding +noncorrosive +noncredit +noncriminal/SM +noncritical +noncrystalline +noncumulative +noncustodial +nondairy +nondeductible/M +nondelivery/SM +nondemocratic +nondenominational +nondepartmental +nondepreciating +nondescript +nondestructive +nondetachable +nondeterminism +nondeterministic +nondisciplinary +nondisclosure/M +nondiscrimination/M +nondiscriminatory +nondramatic +nondrinker/MS +nondrying +none +noneducational +noneffective +nonelastic +nonelectric +nonelectrical +nonempty +nonenforceable +nonentity/SM +nonequivalent/MS +nonessential +nonesuch/MS +nonetheless +nonevent/MS +nonexchangeable +nonexclusive +nonexempt/M +nonexistence/M +nonexistent +nonexplosive/MS +nonfactual +nonfading +nonfat +nonfatal +nonfattening +nonferrous +nonfiction/M +nonfictional +nonflammable +nonflowering +nonfluctuating +nonflying +nonfood/M +nonfreezing +nonfunctional +nongovernmental +nongranular +nonhazardous +nonhereditary +nonhuman +nonidentical +noninclusive +nonindependent +nonindustrial +noninfectious +noninflammatory +noninflationary +noninflected +nonintellectual/MS +noninterchangeable +noninterference/M +nonintervention/M +nonintoxicating +noninvasive +nonirritating +nonissue +nonjudgmental +nonjudicial +nonlegal +nonlethal +nonlinear +nonliterary +nonliving/M +nonmagnetic +nonmalignant +nonmember/MS +nonmetal/SM +nonmetallic +nonmigratory +nonmilitant +nonmilitary +nonnarcotic/SM +nonnative/MS +nonnegotiable +nonnuclear +nonnumerical +nonobjective +nonobligatory +nonobservance/M +nonobservant +nonoccupational +nonoccurence +nonofficial +nonoperational +nonoperative +nonparallel/MS +nonpareil/MS +nonparticipant/MS +nonparticipating +nonpartisan/SM +nonpaying +nonpayment/SM +nonperformance/M +nonperforming +nonperishable +nonperson/MS +nonphysical/Y +nonplus/S +nonplussed +nonplussing +nonpoisonous +nonpolitical +nonpolluting +nonporous +nonpracticing +nonprejudicial +nonprescription +nonproductive +nonprofessional/SM +nonprofit/SMB +nonproliferation/M +nonpublic +nonpunishable +nonracial +nonradioactive +nonrandom +nonreactive +nonreciprocal/SM +nonreciprocating +nonrecognition/M +nonrecoverable +nonrecurring +nonredeemable +nonrefillable +nonrefundable +nonreligious +nonrenewable +nonrepresentational +nonresident/MS +nonresidential +nonresidual/M +nonresistance/M +nonresistant +nonrestrictive +nonreturnable/MS +nonrhythmic +nonrigid +nonsalaried +nonscheduled +nonscientific +nonscoring +nonseasonal +nonsectarian +nonsecular +nonsegregated +nonsense/M +nonsensical/Y +nonsensitive +nonsexist +nonsexual +nonskid +nonslip +nonsmoker/SM +nonsmoking +nonsocial +nonspeaking +nonspecialist/MS +nonspecializing +nonspecific +nonspiritual/SM +nonstaining +nonstandard +nonstarter/MS +nonstick +nonstop +nonstrategic +nonstriking +nonstructural +nonsuccessive +nonsupport/GM +nonsurgical +nonsustaining +nonsympathizer/M +nontarnishable +nontaxable +nontechnical +nontenured +nontheatrical +nonthinking +nonthreatening +nontoxic +nontraditional +nontransferable +nontransparent +nontrivial +nontropical +nonuniform +nonunion +nonuser/MS +nonvenomous +nonverbal +nonviable +nonviolence/M +nonviolent/Y +nonvirulent +nonvocal +nonvocational +nonvolatile +nonvoter/MS +nonvoting +nonwhite/MS +nonworking +nonyielding +nonzero +noodle/MGDS +nook/MS +nookie +nooky +noon/M +noonday/M +noontide/M +noontime/M +noose/SM +nope +nor +nor'easter +norm/MS +normal/MY +normalcy/M +normality/M +normalization/M +normalize/DSG +normative +north/ZMR +northbound +northeast/MRZ +northeaster/MY +northeastern +northeastward/S +norther/MY +northerly/SM +northern/ZR +northerner/M +northernmost +northward/S +northwest/ZMR +northwester/MY +northwestern +northwestward/S +nose/MGDS +nosebag/S +nosebleed/MS +nosecone/SM +nosedive/DSMG +nosegay/SM +nosh/MDRSZG +nosher/M +nosily +nosiness/M +nostalgia/M +nostalgic +nostalgically +nostril/MS +nostrum/MS +nosy/RPT +not/B +notability/SM +notable/SM +notably +notarial +notarization/M +notarize/GDS +notary/SM +notate/GDS +notation/FCSM +notch/GMDS +note's +note/FCSDG +notebook/MS +notelet/S +notepad/S +notepaper/M +noteworthiness/M +noteworthy/P +nothing/PSM +nothingness/M +notice/MGDS +noticeable/U +noticeably +noticeboard/S +noticed/U +notifiable +notification/M +notifier/M +notify/NDRSXZG +notion/MS +notional/Y +notoriety/M +notorious/Y +notwithstanding +notwork/S +nougat/MS +noun/KMS +nourish/DSLG +nourishment/M +nous +nova/MS +novae +novel/SM +novelette/SM +novelist/SM +novelization/MS +novelize/DSG +novella/MS +novelty/SM +novena/MS +novene +novice/MS +novitiate/MS +now/M +nowadays/M +noway/S +nowhere/M +nowise +nowt +noxious +nozzle/MS +nu/SM +nuance/MDS +nub/SM +nubbin/MS +nubby/TR +nubile +nuclear/K +nucleate/DSGN +nucleation/M +nuclei +nucleic +nucleoli +nucleolus/M +nucleon/SM +nucleoside +nucleotide +nucleus/M +nude/MTRS +nudge/GDSM +nudism/M +nudist/SM +nudity/M +nugatory +nugget/SM +nuisance/MS +nuke/MGDS +null/S +nullification/M +nullify/NDSG +nullity/M +numb/ZTGPDRYS +number's +number/ASDG +numbered/U +numberless +numbness/M +numerable/I +numeracy/IM +numeral/SM +numerate/XGNDS +numeration/M +numerator/MS +numeric +numerical/Y +numerologist/MS +numerology/M +numerous/Y +numinous +numismatic/S +numismatics/M +numismatist/SM +numskull/MS +nun/SM +nuncio/SM +nunnery/SM +nuptial/MS +nurse/MZGDRS +nurselings +nursemaid/MS +nurser/M +nursery/SM +nurseryman/M +nurserymen +nursing/M +nursling/SM +nurture/DRSMZG +nurturer/M +nut/SM +nutcase/S +nutcracker/MS +nuthatch/MS +nuthouse/S +nutmeat/SM +nutmeg/SM +nutpick/SM +nutria/SM +nutrient/MS +nutriment/MS +nutrition/M +nutritional/Y +nutritionist/SM +nutritious/YP +nutritiousness/M +nutritive +nutshell/MS +nutted +nutter/S +nuttiness/M +nutting +nutty/RTP +nuzzle/DRSMZG +nuzzler/M +nybble/S +nylon/MS +nylons/M +nymph/M +nymphet/MS +nympho/S +nymphomania/M +nymphomaniac/SM +nymphs +o +o'clock +o'er +oaf/SM +oafish/PY +oafishness/M +oak/SMN +oakum/M +oar/SGMD +oarlock/SM +oarsman/M +oarsmen +oarswoman/M +oarswomen +oases +oasis/M +oat/SMN +oatcake/SM +oath/M +oaths +oatmeal/M +oats/M +ob/S +obbligato/MS +obduracy/M +obdurate/PY +obdurateness/M +obedience/EM +obedient/EY +obeisance/SM +obeisant +obelisk/MS +obese +obesity/M +obey/EDSG +obfuscate/GNXDS +obfuscation/M +obi/SM +obit/MS +obituary/SM +obj +object/SGVMD +objectify/NGDS +objection/SMB +objectionable/U +objectionably +objective/SMYP +objectiveness/M +objectivity/M +objector/MS +objurgate/XGNDS +objurgation/M +oblate/NX +oblation/M +obligate/DSXGN +obligation/M +obligatorily +obligatory +oblige/EGDS +obliging/Y +oblique/SMYP +obliqueness/M +obliquity/M +obliterate/DSGN +obliteration/M +oblivion/M +oblivious/YP +obliviousness/M +oblong/MS +obloquy/M +obnoxious/YP +obnoxiousness/M +oboe/MS +oboist/MS +obscene/RYT +obscenity/SM +obscurantism/M +obscurantist/SM +obscure/DRSYTG +obscurity/SM +obsequies +obsequious/PY +obsequiousness/M +obsequy/M +observably +observance/MS +observant/Y +observation/SM +observational +observatory/SM +observe/DRSBZG +observed/U +observer/M +obsess/DSGV +obsession/SM +obsessional/Y +obsessive/PSMY +obsessiveness/M +obsidian/M +obsolesce/DSG +obsolescence/M +obsolescent +obsolete/GDS +obstacle/MS +obstetric/S +obstetrical +obstetrician/SM +obstetrics/M +obstinacy/M +obstinate/Y +obstreperous/YP +obstreperousness/M +obstruct/DGVS +obstructed/U +obstruction/SM +obstructionism/M +obstructionist/MS +obstructive/YP +obstructiveness/M +obtain/DBLGS +obtainable/U +obtainment/M +obtrude/DSG +obtrusion/M +obtrusive/UPY +obtrusiveness/UM +obtuse/YTRP +obtuseness/M +obverse/SM +obviate/DSGN +obviation/M +obvious/PY +obviousness/M +ocarina/MS +occasion/GMDS +occasional/Y +occidental/SM +occlude/GDS +occlusion/SM +occlusive +occult/M +occultism/M +occultist/SM +occupancy/M +occupant/SM +occupation/AM +occupational/Y +occupations +occupied/U +occupier/SM +occupy/ADSG +occur/AS +occurred/A +occurrence/SM +occurring/A +ocean/SM +oceanfront/SM +oceangoing +oceanic/M +oceanographer/SM +oceanographic +oceanography/M +oceanology/M +ocelot/MS +och/R +ocher/M +ocker/S +octagon/MS +octagonal +octal +octane/MS +octave/MS +octavo/MS +octet/SM +octogenarian/SM +octopus/MS +ocular/MS +oculist/SM +oculomotor +odalisque/SM +odd/STRYLP +oddball/SM +oddity/SM +oddment/SM +oddness/M +odds/M +ode/SM +odious/YP +odiousness/M +odium/M +odometer/MS +odor/MDS +odoriferous +odorless +odorous +odyssey/MS +oedipal +oenology/M +oenophile/SM +oeuvre/MS +of +off/SZGDRJ +offal/M +offbeat/MS +offend/ZGDRS +offender/M +offense/MS +offensive's +offensive/IPY +offensiveness/IM +offensives +offer/JGMD +offering/M +offertory/SM +offhand +offhanded/PY +offhandedness/M +office/MZRS +officeholder/SM +officer/M +official/MYS +officialdom/M +officialese +officialism/M +officiant/SM +officiate/DSG +officiator/MS +officious/PY +officiousness/M +offing/M +offish +offline +offload/SDG +offprint/SM +offset/MS +offsetting +offshoot/MS +offshore/G +offside +offsite +offspring/M +offstage/S +offtrack +oft +often/TR +oftentimes +ofttimes +ogle/MZGDRS +ogler/M +ogre/MS +ogreish +ogress/MS +oh/M +ohm/SM +ohmmeter/MS +oho +ohs +oi +oik/S +oil/SGMD +oilcan/S +oilcloth/M +oilcloths +oilfield/S +oiliness/M +oilman +oilmen +oilskin/MS +oilskins/M +oily/RPT +oink/MDSG +ointment/SM +okapi/SM +okay/MSG +okra/MS +old/TMNRP +oldie/SM +oldish +oldness/M +oldster/MS +ole/SMV +oleaginous +oleander/MS +oleo/M +oleomargarine/M +olfactory/SM +oligarch/M +oligarchic +oligarchical +oligarchs +oligarchy/SM +oligonucleotide/S +oligopoly/SM +olive/SM +om/SMNX +ombudsman/M +ombudsmen +omega/SM +omelet/MS +omen/M +omicron/MS +ominous/YP +ominousness/M +omission/MS +omit/S +omitted +omitting +omnibus/MS +omnipotence/M +omnipotent +omnipresence/M +omnipresent +omniscience/M +omniscient +omnivore/MS +omnivorous/PY +omnivorousness/M +on/Y +onboard +once/M +oncogene/SM +oncologist/SM +oncology/M +oncoming +one/SXMNP +oneness/M +onerous/PY +onerousness/M +oneself +onetime +ongoing +onion/M +onionskin/M +online +onlooker/SM +onlooking +onomatopoeia/M +onomatopoeic +onomatopoetic +onrush/MSG +onscreen +onset/MS +onshore +onside +onsite +onslaught/MS +onstage +onto +ontogeny/M +ontological +ontology/M +onus/MS +onward +onyx/MS +oodles/M +ooh/GD +oohs +oomph +oops +ooze/MGDS +oozy/TR +op/SMDG +opacity/M +opal/MS +opalescence/M +opalescent +opaque/PYTGDRS +opaqueness/M +opcode/S +ope/S +open/ZTGJPMDRYS +opencast +opened/U +opener/M +openhanded/P +openhandedness/M +openhearted +opening/M +openness/M +openwork/M +opera/MS +operable/I +operand/S +operate/DSGNVX +operatic +operatically +operation/M +operational/Y +operative/SM +operator/SM +operetta/SM +ophthalmic +ophthalmologist/SM +ophthalmology/M +opiate/SM +opine/GNXDS +opinion/M +opinionated +opioid/SM +opium/M +opossum/MS +opp +opponent/SM +opportune/IY +opportunism/M +opportunist/SM +opportunistic +opportunistically +opportunity/SM +oppose/DSG +opposed/U +opposite/SMYNX +opposition/M +oppress/DSGV +oppression/M +oppressive/YP +oppressiveness/M +oppressor/MS +opprobrious/Y +opprobrium/M +opt/SGD +optic/MS +optical/Y +optician/SM +optics/M +optima +optimal/Y +optimism/SM +optimist/SM +optimistic +optimistically +optimization/MS +optimize/DRSG +optimum/SM +option/SMDG +optional/Y +optometrist/MS +optometry/M +opulence/M +opulent/Y +opus/MS +or +oracle/SM +oracular +oral/MYS +orality +orange/SMP +orangeade/MS +orangery/SM +orangutan/SM +orate/GNXDS +oration/M +orator/SM +oratorical/Y +oratorio/MS +oratory/SM +orb/SM +orbicular +orbit/MDRZGS +orbital/SM +orbiter/M +orc/SM +orchard/SM +orchestra/MS +orchestral +orchestrate/DSXGN +orchestration/M +orchid/SM +ordain/SDLG +ordainment/M +ordeal/SM +order/EAMDGS +ordered/U +orderings +orderliness/EM +orderly/PSM +ordinal/SM +ordinance/SM +ordinarily +ordinariness/M +ordinary/SMP +ordinate/MNSX +ordination/M +ordnance/M +ordure/M +ore/SM +oregano/M +org +organ/MS +organdy/M +organelle/MS +organic/SM +organically/I +organism/MS +organismic +organist/MS +organization/ASM +organizational/Y +organize/AESDG +organized/U +organizer/MS +organza/M +orgasm/SM +orgasmic +orgiastic +orgy/SM +oriel/MS +orient's +orient/AEDGS +oriental/MS +orientalist/S +orientate/EDSGN +orientation/AEM +orientations +orienteering +orifice/MS +orig +origami/M +origin/SM +original/MYS +originality/M +originate/DSGN +origination/M +originator/SM +oriole/SM +orison/SM +ormolu/M +ornament/SGMD +ornamental +ornamentation/M +ornate/YP +ornateness/M +orneriness/M +ornery/PRT +ornithological +ornithologist/MS +ornithology/M +orotund +orotundity/SM +orphan/SMDG +orphanage/MS +orris/MS +orthodontia/M +orthodontic/S +orthodontics/M +orthodontist/SM +orthodox/U +orthodoxy/SM +orthogonal +orthogonality +orthographic +orthographically +orthography/SM +orthopedic/S +orthopedics/M +orthopedist/MS +orzo/M +oscillate/GNDSX +oscillation/M +oscillator/SM +oscillatory +oscilloscope/MS +osculate/DSXGN +osculation/M +osier/MS +osmium/M +osmosis/M +osmotic +osprey/SM +ossicles +ossification/M +ossify/NGDS +ostensible +ostensibly +ostentation/M +ostentatious/Y +osteoarthritis/M +osteopath/M +osteopathic +osteopaths +osteopathy/M +osteoporosis/M +ostler/S +ostracism/M +ostracize/GDS +ostrich/MS +other/MSP +otherwise +otherworldly +otiose +otter/MS +ottoman/MS +oubliette/MS +ouch +ought +oughtn't +ounce/MS +our/S +ourselves +oust/ZGDRS +ouster/M +out/SJGMDR +outage/SM +outargue/GDS +outback/MS +outbalance/DSG +outbid/S +outbidding +outboard/MS +outboast/DSG +outbound +outbox/MS +outbreak/MS +outbuilding/MS +outburst/SM +outcast/MS +outclass/DSG +outcome/MS +outcrop/MS +outcropped +outcropping/SM +outcry/SM +outdated +outdid +outdistance/GDS +outdo/G +outdoes +outdone +outdoor/S +outdoors/M +outdoorsy +outdraw/GS +outdrawn +outdrew +outercourse +outermost +outerwear/M +outface/GDS +outfall/S +outfield/SMRZ +outfielder/M +outfight/SG +outfit/SM +outfitted +outfitter/MS +outfitting +outflank/GSD +outflow/MS +outfought +outfox/GDS +outgo/MJG +outgoes +outgrew +outgrow/HGS +outgrown +outgrowth/M +outgrowths +outguess/GDS +outgun/S +outgunned +outgunning +outhit/S +outhitting +outhouse/SM +outing/M +outlaid +outlandish/PY +outlandishness/M +outlast/DSG +outlaw/SGMD +outlay/SGM +outlet/SM +outlier/S +outline/MGDS +outlive/GDS +outlook/MS +outlying +outmaneuver/GDS +outmatch/GDS +outmoded +outnumber/DSG +outpace/GDS +outpatient/MS +outperform/GSD +outplace/L +outplacement/M +outplay/GDS +outpoint/DGS +outpost/MS +outpouring/MS +outproduce/DSG +output/SM +outputted +outputting +outrace/GDS +outrage/MGDS +outrageous/Y +outran +outrank/GDS +outre +outreach/MDSG +outrider/MS +outrigger/SM +outright +outrun/S +outrunning +outscore/GDS +outsell/GS +outset/SM +outshine/GS +outshone +outshout/GDS +outside/MZRS +outsider/M +outsize/MS +outskirt/MS +outsmart/GDS +outsold +outsource/DSG +outsourcing/M +outspend/SG +outspent +outspoken/YP +outspokenness/M +outspread/GS +outstanding/Y +outstation/MS +outstay/DGS +outstretch/DSG +outstrip/S +outstripped +outstripping +outta +outtake/MS +outvote/GDS +outward/YS +outwear/GS +outweigh/GD +outweighs +outwit/S +outwith +outwitted +outwitting +outwore +outwork/MDRSZG +outworn +ouzo/MS +ova +oval/MS +ovarian +ovary/SM +ovate/NX +ovation/M +oven/MS +ovenbird/SM +ovenproof +ovenware +over/MYS +overabundance/M +overabundant +overachieve/ZGDRS +overachiever/M +overact/GVSD +overage/SM +overaggressive +overall/SM +overalls/M +overambitious +overanxious +overarching +overarm/GSD +overate +overattentive +overawe/DSG +overbalance/MGDS +overbear/GS +overbearing/Y +overbid/SM +overbidding +overbite/MS +overblown +overboard +overbold +overbook/DGS +overbore +overborne +overbought +overbuild/SG +overbuilt +overburden/GSD +overbuy/GS +overcame +overcapacity/M +overcapitalize/DSG +overcareful +overcast/MGS +overcautious +overcharge/DSMG +overclock/GD +overcloud/SGD +overcoat/MS +overcome/GS +overcompensate/DSGN +overcompensation/M +overconfidence/M +overconfident +overconscientious +overcook/DGS +overcritical +overcrowd/SDG +overcrowding/M +overdecorate/DSG +overdependent +overdevelop/SDG +overdid +overdo/G +overdoes +overdone +overdose/MGDS +overdraft/SM +overdraw/GS +overdrawn +overdress/GMDS +overdrew +overdrive/SM +overdub/SM +overdubbed +overdubbing +overdue +overeager +overeat/GSN +overemotional +overemphasis/M +overemphasize/GDS +overenthusiastic +overestimate/MGNDS +overestimation/M +overexcite/DSG +overexercise/GDS +overexert/SDG +overexertion/M +overexpose/GDS +overexposure/M +overextend/DGS +overfed +overfeed/GS +overfill/DGS +overflew +overflight/MS +overflow/MDSG +overflown +overfly/GS +overfond +overfull +overgeneralize/DSG +overgenerous +overgraze/DSG +overgrew +overground +overgrow/HSG +overgrown +overgrowth/M +overhand/MDS +overhang/MSG +overhasty +overhaul/MDSG +overhead/MS +overhear/SG +overheard +overheat/DSG +overhung +overindulge/GDS +overindulgence/M +overindulgent +overinflated +overjoy/GSD +overkill/M +overladen +overlaid +overlain +overland +overlap/SM +overlapped +overlapping +overlarge +overlay/GSM +overleaf +overlie +overload/GMDS +overlong +overlook/GMDS +overlord/MS +overly/SG +overmanned +overmanning +overmaster/SDG +overmodest +overmuch/S +overnice +overnight/MS +overoptimism/M +overoptimistic +overpaid +overparticular +overpass/MS +overpay/GS +overplay/GDS +overpopulate/GNDS +overpopulation/M +overpower/SDG +overpowering/Y +overpraise/DSG +overprecise +overprice/DSG +overprint/SMDG +overproduce/GDS +overproduction/M +overprotect/SDGV +overqualified +overran +overrate/GDS +overreach/GDS +overreact/SDG +overreaction/SM +overrefined +overridden +override/MGS +overripe/M +overrode +overrule/GDS +overrun/SM +overrunning +oversampling +oversaw +oversea/S +oversee/RSZ +overseeing +overseen +overseer/M +oversell/GS +oversensitive/P +oversensitiveness/M +oversexed +overshadow/DSG +overshare/DSG +overshoe/MS +overshoot/GS +overshot +oversight/SM +oversimple +oversimplification/M +oversimplify/DSNGX +oversize +oversleep/GS +overslept +oversold +overspecialization/M +overspecialize/GDS +overspend/SG +overspent +overspread/GS +overstaffed +overstate/DSLG +overstatement/MS +overstay/DSG +overstep/S +overstepped +overstepping +overstimulate/DSG +overstock/GSD +overstretch/GDS +overstrict +overstrung +overstuffed +oversubscribe/DSG +oversubtle +oversupply/GDS +oversuspicious +overt/Y +overtake/GS +overtaken +overtax/GDS +overthink/SG +overthought +overthrew +overthrow/SMG +overthrown +overtime/MS +overtire/GDS +overtone/MS +overtook +overture/MS +overturn/DSG +overuse/DSMG +overvaluation/S +overvalue/DSG +overview/MS +overweening/Y +overweight/M +overwhelm/SGD +overwhelming/Y +overwinter/SDG +overwork/GMDS +overwrite/GS +overwritten +overwrote +overwrought +overzealous +oviduct/SM +oviparous +ovoid/MS +ovular +ovulate/DSGN +ovulation/M +ovule/MS +ovum/M +ow +owe/DSG +owl/SM +owlet/MS +owlish/Y +own/ESGD +owner/MS +ownership/M +ox/MN +oxalate +oxblood/M +oxbow/MS +oxcart/SM +oxford/SM +oxidant/MS +oxidase +oxidation/M +oxidative +oxide/MS +oxidization/M +oxidize/ZGDRS +oxidizer/M +oxtail/S +oxyacetylene/M +oxygen/M +oxygenate/DSGN +oxygenation/M +oxymora +oxymoron/M +oyster/SM +oz +ozone/M +p/NRXTGJ +pH +pa/SMH +pablum/M +pabulum/M +pace/MZGDRS +pacemaker/SM +pacer/M +pacesetter/SM +pacey +pachyderm/MS +pachysandra/MS +pacific +pacifically +pacification/M +pacifier/M +pacifism/M +pacifist/SM +pacifistic +pacify/ZGDRSN +pack's +pack/AUGSD +package's +package/AGDS +packager/SM +packaging/M +packer/MS +packet/MS +packing's +packinghouse/SM +packsaddle/MS +pact/MS +pacy/RT +pad/SM +padded +padding/M +paddle/MZGDRS +paddler/M +paddock/MDGS +paddy/SM +padlock/MDSG +padre/SM +paean/SM +paella/MS +pagan/SM +paganism/M +page/MZGDRS +pageant/MS +pageantry/M +pageboy/SM +pager/M +paginate/DSGN +pagination/M +pagoda/MS +pah +paid/AU +pail/MS +pailful/SM +pain/MDSG +painful/PY +painfuller +painfullest +painfulness/M +painkiller/MS +painkilling +painless/PY +painlessness/M +painstaking/MY +paint/SZGJMDR +paintball +paintbox/MS +paintbrush/MS +painted/U +painter/MY +painting/M +paintwork +pair/AMDSG +paired/U +pairing/S +pairwise +paisley/SM +pajama/S +pajamas/M +pal/SMY +palace/MS +paladin/SM +palanquin/SM +palatable/U +palatal/SM +palatalization/M +palatalize/GDS +palate/MBS +palatial/Y +palatinate/MS +palatine/MS +palaver/GSMD +palazzi +palazzo +pale/MYTGPDRSJ +paleface/MS +paleness/M +paleo +paleographer/MS +paleography/M +paleolithic +paleontologist/SM +paleontology/M +palette/SM +palfrey/SM +palimony/M +palimpsest/MS +palindrome/MS +palindromic +paling/M +palisade/SM +palish +pall/MDSG +palladium/M +pallbearer/MS +pallet/MS +palliate/DSGNV +palliation/M +palliative/SM +pallid/YP +pallidness/M +pallor/M +palm/MDSG +palmate +palmetto/SM +palmist/SM +palmistry/M +palmtop/SM +palmy/TR +palomino/MS +palpable +palpably +palpate/DSGN +palpation/M +palpitate/XGNDS +palpitation/M +palsy/GDSM +paltriness/M +paltry/RPT +pampas/M +pamper/DSG +pamphlet/MS +pamphleteer/MS +pan/SM +panacea/SM +panache/M +panama/MS +panatella/S +pancake/DSMG +panchromatic +pancreas/MS +pancreatic +pancreatitis +panda/SM +pandemic/SM +pandemonium/M +pander/MDRZGS +panderer/M +pane/KM +panegyric/SM +panel/SGJMD +paneling/M +panelist/MS +panes +pang/MS +panhandle/DRSMZG +panhandler/M +panic/SM +panicked +panicking +panicky +panned +pannier/SM +panning +panoply/SM +panorama/SM +panoramic +panpipes/M +pansy/SM +pant/MDSG +pantaloons/M +pantechnicon/S +pantheism/M +pantheist/SM +pantheistic +pantheon/SM +panther/MS +pantie/MS +panto/S +pantomime/MGDS +pantomimic +pantomimist/SM +pantry/SM +pantsuit/SM +pantyhose/M +pantyliner/M +pantywaist/SM +pap/SM +papa/MS +papacy/SM +papal +paparazzi/M +paparazzo +papaya/MS +paper/SZGMDR +paperback/SM +paperbark/S +paperboard/M +paperboy/SM +paperclip/S +paperer/M +papergirl/SM +paperhanger/SM +paperhanging/M +paperless +paperweight/MS +paperwork/M +papery +papilla/M +papillae +papillary +papist/MS +papoose/MS +pappy/SM +paprika/M +papyri +papyrus/M +par/SZGMDRBJ +para/MS +parable/MS +parabola/SM +parabolic +paracetamol/S +parachute/DSMG +parachutist/MS +parade/MZGDRS +parader/M +paradigm/SM +paradigmatic +paradisaical +paradise/SM +paradox/MS +paradoxical/Y +paraffin/M +paragliding +paragon/MS +paragraph/GMD +paragraphs +parakeet/SM +paralegal/MS +parallax/MS +parallel/SGMD +paralleled/U +parallelism/MS +parallelization +parallelized +parallelogram/SM +paralyses +paralysis/M +paralytic/SM +paralyze/DSG +paralyzing/Y +paramagnetic +paramecia +paramecium/M +paramedic/MS +paramedical/MS +parameter/MS +parameterize/D +parametric +paramilitary/SM +paramount +paramountcy +paramour/SM +paranoia/M +paranoiac/MS +paranoid/SM +paranormal +parapet/MS +paraphernalia/M +paraphrase/DSMG +paraplegia/M +paraplegic/SM +paraprofessional/MS +parapsychologist/MS +parapsychology/M +paraquat/M +parasailing +parascending +parasite/SM +parasitic +parasitical/Y +parasitism/M +parasol/MS +parasympathetic/S +parathion/M +parathyroid/MS +paratroop/RZS +paratrooper/M +paratroops/M +paratyphoid/M +parboil/DSG +parcel/GMDS +parch/LGDS +parchment/SM +pardner/S +pardon/ZGMDRBS +pardonable/U +pardonably/U +pardoner/M +pare/S +paregoric/M +parent/GMDS +parentage/M +parental +parentheses +parenthesis/M +parenthesize/DSG +parenthetic +parenthetical/Y +parenthood/M +parenting/M +parer/M +pares/S +paresis/M +parfait/MS +pariah/M +pariahs +paribus +parietal +parimutuel/MS +paring/M +parish/MS +parishioner/MS +parity/ESM +park/MDSG +parka/SM +parking/M +parkland +parkour +parkway/MS +parky +parlance/M +parlay/GMDS +parley/GMDS +parliament/SM +parliamentarian/SM +parliamentary +parlor/MS +parlous +parmigiana +parochial/Y +parochialism/M +parodist/SM +parody/GDSM +parole/MGDS +parolee/MS +parotid +paroxysm/SM +paroxysmal +parquet/MDSG +parquetry/M +parred +parricidal +parricide/MS +parring +parrot/GMDS +parry/GDSM +parse/DRSG +parsec/MS +parsimonious/Y +parsimony/M +parsley/M +parsnip/MS +parson/MS +parsonage/MS +part's +part/CDSG +partake/ZGRS +partaken +partaker/M +parterre/SM +parthenogenesis/M +partial/MYS +partiality/M +participant/SM +participate/DSGN +participation/M +participator/MS +participatory +participial/M +participle/MS +particle/SM +particleboard/M +particular/SMY +particularity/SM +particularization/M +particularize/DSG +particulate/SM +parting/MS +partisan/SM +partisanship/M +partition/GMDS +partitive/MS +partly +partner/MDSG +partnership/MS +partook +partridge/SM +parturition/M +partway +party/GDSM +parvenu/MS +pascal/MS +paschal +pasha/SM +pass/M +passably +passage/MS +passageway/MS +passbook/MS +passe/DRSBXZGNV +passel/MS +passenger/SM +passer/M +passerby/M +passersby +passim +passing/MY +passion/EM +passionate/EY +passionflower/SM +passionless +passive/PMYS +passiveness/M +passivity/M +passivization +passivize/DSG +passkey/MS +passphrase/S +passport/MS +password/MS +past/AMS +pasta/SM +paste/DSMG +pasteboard/M +pastel/MS +pastern/MS +pasteurization/M +pasteurize/ZGDRS +pasteurized/U +pasteurizer/M +pastiche/MS +pastie +pastille/MS +pastime/MS +pastiness/M +pastor/MS +pastoral/MS +pastorate/MS +pastrami/M +pastry/SM +pasturage/M +pasture/DSMG +pastureland/M +pasty/PTRSM +pat/SM +patch/EGMDS +patchily +patchiness/M +patchouli +patchwork/SM +patchy/TPR +pate/MS +patella/MS +patellae +patent/GMDYS +paterfamilias/MS +paternal/Y +paternalism/M +paternalist/S +paternalistic +paternity/M +paternoster/MS +path/M +pathetic +pathetically +pathfinder/SM +pathless +pathogen/SM +pathogenic +pathological/Y +pathologist/SM +pathology/M +pathos/M +paths +pathway/MS +patience/M +patient/IMST +patienter +patiently +patina/MS +patine +patio/SM +patisserie/S +patois/M +patresfamilias +patriarch/M +patriarchal +patriarchate/MS +patriarchs +patriarchy/SM +patrician/SM +patricidal +patricide/SM +patrimonial +patrimony/SM +patriot/SM +patriotic/U +patriotically +patriotism/M +patrol/MS +patrolled +patrolling +patrolman/M +patrolmen +patrolwoman/M +patrolwomen +patron/MS +patronage/MS +patroness/MS +patronize/ZGDRS +patronizer/M +patronizing/Y +patronymic/SM +patronymically +patroon/SM +patsy/SM +patted +patter/MDGS +pattern/SMDG +patting +patty/SM +paucity/M +paunch/MS +paunchy/RT +pauper/MS +pauperism/M +pauperize/DSG +pause/DSMG +pave/AGDS +paved/U +pavement/MS +pavilion/SM +paving/MS +pavlova/S +paw/SGMD +pawl/MS +pawn/MDSG +pawnbroker/MS +pawnbroking/M +pawnshop/MS +pawpaw/MS +pay's +pay/ASGBL +payback/SM +paycheck/MS +payday/MS +payed +payee/SM +payer/SM +payload/SM +paymaster/SM +payment/ASM +payoff/MS +payola/M +payout/MS +payphone/S +payroll/SM +payslip/SM +paywall/SM +payware +pct +pd +pea/SM +peace/SM +peaceable +peaceably +peaceful/PY +peacefulness/M +peacekeeper/SM +peacekeeping/M +peacemaker/MS +peacemaking/M +peacetime/M +peach/MS +peachy/TR +peacock/MS +peafowl/MS +peahen/MS +peak/MDSG +peaky +peal/AMDSG +peanut/MS +pear/MYS +pearl/SGMD +pearly/RT +peasant/SM +peasantry/M +peashooter/SM +peat/M +peaty/TR +pebble/MGDS +pebbly +pecan/SM +peccadillo/M +peccadilloes +peccary/SM +peck/MDRSZG +peckish +pecs +pectic +pectin/M +pectoral/MS +pectoralis +peculate/GNDS +peculation/M +peculator/SM +peculiar/Y +peculiarity/SM +pecuniary +pedagogic +pedagogical/Y +pedagogue/SM +pedagogy/M +pedal/SGMD +pedalo/S +pedant/MS +pedantic +pedantically +pedantry/M +peddle/ZGDRS +peddler/M +pederast/MS +pederasty/M +pedestal/MS +pedestrian/SM +pedestrianization +pedestrianize/GDS +pediatric/S +pediatrician/MS +pediatrics/M +pedicab/SM +pedicure/MGDS +pedicurist/MS +pedigree/MDS +pediment/MS +pedometer/MS +pedophile/S +pedophilia +peduncle/MS +pee/DRSMZ +peeing +peek/MDSG +peekaboo/M +peel/MDRSJZG +peeled/U +peeler/M +peeling/M +peen/MS +peep/MDRSZG +peepbo +peeper/M +peephole/MS +peepshow/MS +peer/MDG +peerage/SM +peeress/MS +peerless +peeve/DSMG +peevish/PY +peevishness/M +peewee/MS +peewit/S +peg/SM +pegboard/MS +pegged +pegging +peignoir/SM +pejoration/M +pejorative/SMY +peke/MS +pekineses +pekingese/SM +pekoe/M +pelagic +pelf/M +pelican/MS +pellagra/M +pellet/GMDS +pellucid +pelmet/S +pelt/MDSG +pelvic +pelvis/MS +pemmican/M +pen/M +penal +penalization/M +penalize/DSG +penalty/SM +penance/MS +pence +penchant/SM +pencil/GMDJS +pend/CDSG +pendant/MS +pendent/MS +pendulous +pendulum/MS +penetrability/M +penetrable +penetrate/DSGNVX +penetrating/Y +penetration/M +penfriend/S +penguin/MS +penicillin/M +penile +peninsula/SM +peninsular +penis/MS +penitence/M +penitent/SMY +penitential +penitentiary/SM +penknife/M +penknives +penlight/SM +penman/M +penmanship/M +penmen +pennant/MS +penned +penniless +penning +pennon/MS +penny/SM +pennyweight/MS +pennyworth +penologist/MS +penology/M +pension/BZGMDRS +pensioner/M +pensive/PY +pensiveness/M +pent +pentacle/MS +pentagon/MS +pentagonal +pentagram/SM +pentameter/SM +pentathlete/MS +pentathlon/MS +penthouse/SM +penuche/M +penultimate/SM +penumbra/MS +penumbrae +penurious/PY +penuriousness/M +penury/M +peon/MS +peonage/M +peony/SM +people/MGDS +pep/SM +pepped +pepper/GMDS +peppercorn/SM +peppermint/SM +pepperoni/MS +peppery +peppiness/M +pepping +peppy/TPR +pepsin/M +peptic/MS +peptide/S +peradventure/M +perambulate/XGNDS +perambulation/M +perambulator/MS +percale/MS +perceive/BGDS +perceived/U +percent/MS +percentage/SM +percentile/SM +perceptible +perceptibly +perception/SM +perceptional +perceptive/PY +perceptiveness/M +perceptual/Y +perch/GMDS +perchance +percipience/M +percipient +percolate/GNDS +percolation/M +percolator/SM +percussion/AM +percussionist/MS +percussive +perdition/M +perdurable +peregrinate/DSXGN +peregrination/M +peregrine/MS +peremptorily +peremptory +perennial/SMY +perestroika/M +perfect/PTGMDRYS +perfecta/MS +perfectibility/M +perfectible +perfection/SM +perfectionism/M +perfectionist/SM +perfectness/M +perfidious/Y +perfidy/SM +perforate/GNXDS +perforation/M +perforce +perform/SDRZG +performance/SM +performative +performed/U +performer/M +perfume/DRSMZG +perfumer/M +perfumery/SM +perfunctorily +perfunctory +perfusion +pergola/SM +perhaps +pericardia +pericardial +pericarditis +pericardium/M +perigee/SM +perihelia +perihelion/M +peril/SGMD +perilous/Y +perimeter/SM +perinatal +perinea +perineum/M +period/MS +periodic +periodical/SMY +periodicity/M +periodontal +periodontics/M +periodontist/SM +peripatetic/MS +peripheral/MYS +periphery/SM +periphrases +periphrasis/M +periphrastic +periscope/SM +perish/BDRSZG +perishable/MS +peristalses +peristalsis/M +peristaltic +peristyle/SM +peritoneal +peritoneum/MS +peritonitis/M +periwig/SM +periwinkle/SM +perjure/DRSZG +perjurer/M +perjury/SM +perk/MDSG +perkily +perkiness/M +perky/TPR +perm/MDSG +permafrost/M +permanence/M +permanency/M +permanent/SMY +permeability/M +permeable +permeate/GNDS +permeation/M +permissible +permissibly +permission/MS +permissive/PY +permissiveness/M +permit/MS +permitted +permitting +permittivity +permutation/SM +permute/DSG +pernicious/YP +perniciousness/M +peroration/MS +peroxide/MGDS +perpendicular/SMY +perpendicularity/M +perpetrate/DSGN +perpetration/M +perpetrator/MS +perpetual/SMY +perpetuate/DSGN +perpetuation/M +perpetuity/M +perplex/GDS +perplexed/Y +perplexing/Y +perplexity/SM +perquisite/SM +persecute/GNXDS +persecution/M +persecutor/SM +perseverance/M +persevere/DSG +persiflage/M +persimmon/SM +persist/SGD +persistence/M +persistent/Y +persnickety +person/UMS +persona/SM +personable +personae +personage/MS +personal/MYS +personality/SM +personalize/CDSG +personalty/M +personification/M +personify/GDSNX +personnel/M +perspective/MS +perspex +perspicacious/Y +perspicacity/M +perspicuity/M +perspicuous +perspiration/M +perspire/GDS +persuade/BZGDRS +persuaded/U +persuader/M +persuasion/SM +persuasive/PY +persuasiveness/M +pert/RYPT +pertain/GSD +pertinacious/Y +pertinacity/M +pertinence/M +pertinent/Y +pertness/M +perturb/DGS +perturbation/SM +perturbed/U +pertussis/M +peruke/MS +perusal/MS +peruse/GDS +perv/S +pervade/DSG +pervasive/PY +pervasiveness/M +perverse/PXYN +perverseness/M +perversion/M +perversity/M +pervert/SGMD +peseta/MS +peskily +peskiness/M +pesky/TPR +peso/MS +pessary/S +pessimal +pessimism/M +pessimist/SM +pessimistic +pessimistically +pest/MRSZ +pester/GD +pesticide/MS +pestiferous +pestilence/SM +pestilent +pestilential +pestle/MGDS +pesto/M +pet/SZMR +petabyte/MS +petajoule/S +petal/SMD +petard/MS +petawatt/S +petcock/SM +peter/GMD +petiole/SM +petite/MS +petition/ZGMDRS +petitionary +petitioner/M +petrel/MS +petrifaction/M +petrify/DSG +petrochemical/SM +petrodollar/MS +petrol/M +petrolatum/M +petroleum/M +petrologist/SM +petrology/M +petted +petticoat/MS +pettifog/S +pettifogged +pettifogger/SM +pettifoggery/M +pettifogging +pettily +pettiness/M +petting/M +pettish/Y +petty/PTR +petulance/M +petulant/Y +petunia/MS +pew/SM +pewee/SM +pewit/SM +pewter/MS +peyote/M +pf +pfennig/MS +pg +phaeton/MS +phage/S +phagocyte/SM +phalanger/SM +phalanges +phalanx/MS +phalli +phallic +phallocentric +phallocentrism +phallus/M +phantasm/MS +phantasmagoria/MS +phantasmagorical +phantasmal +phantom/SM +pharaoh/M +pharaohs +pharisaic +pharisee/SM +pharmaceutic/MS +pharmaceutical/SM +pharmaceutics/M +pharmacist/MS +pharmacologic +pharmacological +pharmacologist/SM +pharmacology/M +pharmacopoeia/MS +pharmacotherapy +pharmacy/SM +pharyngeal +pharynges +pharyngitis/M +pharynx/M +phase/DSMG +phaseout/SM +phat +pheasant/MS +phenacetin/M +phenobarbital/M +phenol/M +phenom/MS +phenomena +phenomenal/Y +phenomenological +phenomenology +phenomenon/MS +phenotype +phenytoin +pheromone/MS +phew +phi/SM +phial/SM +philander/ZGDRS +philanderer/M +philandering/M +philanthropic +philanthropically +philanthropist/MS +philanthropy/SM +philatelic +philatelist/MS +philately/M +philharmonic/SM +philippic/MS +philistine/MS +philistinism/M +philodendron/SM +philological +philologist/MS +philology/M +philosopher/MS +philosophic +philosophical/Y +philosophize/DRSZG +philosophizer/M +philosophy/SM +philter/MS +phish/ZGDR +phisher/M +phlebitis/M +phlegm/M +phlegmatic +phlegmatically +phloem/M +phlox/M +phobia/MS +phobic/MS +phoebe/MS +phoenix/MS +phone/DSMG +phonecard/S +phoneme/MS +phonemic +phonemically +phonetic/S +phonetically +phonetician/SM +phonetics/M +phoneyed +phoneying +phonic/S +phonically +phonics/M +phoniness/M +phonograph/M +phonographic +phonographs +phonological/Y +phonologist/MS +phonology/M +phonon +phony/PTGDRSM +phooey +phosphate/MS +phosphodiesterase +phosphor/MS +phosphorescence/M +phosphorescent/Y +phosphoric +phosphorous +phosphorus/M +phosphorylation +photo/SGMD +photocell/MS +photocopier/M +photocopy/DRSMZG +photoelectric +photoelectrically +photoengrave/DRSJZG +photoengraver/M +photoengraving/M +photofinishing/M +photogenic +photogenically +photograph/MDRZG +photographer/M +photographic +photographically +photographs/A +photography/M +photojournalism/M +photojournalist/SM +photometer/MS +photon/MS +photosensitive +photostat/SM +photostatic +photostatted +photostatting +photosynthesis/M +photosynthesize/GDS +photosynthetic +phototropic +phototropism +phototypesetter +phototypesetting +photovoltaic +phrasal +phrase's +phrase/AGDS +phrasebook/S +phraseology/M +phrasing/MS +phreaking +phrenologist/SM +phrenology/M +phyla +phylactery/SM +phylogeny/M +phylum/M +phys +physic/SM +physical/MYS +physicality +physician/SM +physicist/SM +physicked +physicking +physics/M +physio/S +physiognomy/SM +physiography/M +physiologic +physiological/Y +physiologist/MS +physiology/M +physiotherapist/MS +physiotherapy/M +physique/MS +phytoplankton +pi/SMDRHZG +pianissimo/SM +pianist/MS +piano/SM +pianoforte/SM +pianola/S +piaster/MS +piazza/MS +pibroch/M +pibrochs +pic/SM +pica/M +picador/MS +picante +picaresque +picayune +piccalilli/M +piccolo/MS +pick/MDRSJZG +pickax/GMDS +picker/M +pickerel/MS +picket/ZGMDRS +pickings/M +pickle/MGDS +pickpocket/SM +pickup/MS +picky/PTR +picnic/MS +picnicked +picnicker/SM +picnicking +picot/SM +pictogram/S +pictograph/M +pictographs +pictorial/MYS +picture/MGDS +picturesque/PY +picturesqueness/M +piddle/MGDS +piddly +pidgin/MS +pie/SM +piebald/MS +piece/DSMG +piecemeal +piecework/MRZ +pieceworker/M +piecrust/SM +pieing +pier/M +pierce/JGDS +piercing/MY +piety/M +piezoelectric +piffle/MG +pig/SML +pigeon/MS +pigeonhole/DSMG +pigged +piggery/S +pigging +piggish/PY +piggishness/M +piggy/TRSM +piggyback/MDSG +pigheaded/PY +pigheadedness/M +piglet/MS +pigment/MDS +pigmentation/M +pigpen/MS +pigskin/MS +pigsty/SM +pigswill +pigtail/MS +pike/MZGDRS +piker/M +pikestaff/SM +pilaf/SM +pilaster/MS +pilchard/MS +pile/MGDSJ +pileup/MS +pilfer/ZGDRS +pilferage/M +pilferer/M +pilgrim/MS +pilgrimage/MS +piling/M +pill/MDSG +pillage/MZGDRS +pillager/M +pillar/MDS +pillbox/MS +pillion/MS +pillock/S +pillory/GDSM +pillow/GMDS +pillowcase/MS +pillowslip/MS +pilot/DGSM +pilothouse/SM +pimento/MS +pimiento/MS +pimp/GMDYS +pimpernel/MS +pimple/DSM +pimply/RT +pin/SM +pinafore/MS +pinata/MS +pinball/M +pincer/MS +pinch/GMDS +pincushion/MS +pine's +pine/AGDS +pineapple/MS +pinewood/S +piney +pinfeather/SM +ping/GMD +pinhead/SM +pinhole/SM +pinier +piniest +pinion/SMDG +pink/TGPMDRS +pinkeye/M +pinkie/SM +pinkish +pinkness/M +pinko/MS +pinnacle/SM +pinnate +pinned/U +pinning/U +pinny/S +pinochle/M +pinon/MS +pinpoint/SGMD +pinprick/MS +pinsetter/SM +pinstripe/DSM +pint/MS +pinto/MS +pinup/MS +pinwheel/GSMD +pinyin/M +pinyon/SM +pioneer/SGMD +pious/YP +piousness/M +pip/SZGMDR +pipe/MS +pipeline/SM +piper/M +pipette/SM +pipework +piping/M +pipit/MS +pipped +pippin/SM +pipping +pipsqueak/SM +piquancy/M +piquant/Y +pique/MGDS +piracy/M +piranha/SM +pirate/DSMG +piratical/Y +pirogi/M +piroshki/M +pirouette/DSMG +piscatorial +pismire/SM +piss/ZGMDRS +pissoir/S +pistachio/SM +piste/S +pistil/SM +pistillate +pistol/SM +piston/SM +pit/SM +pita/MS +pitapat/SM +pitch/MDRSZG +pitchblende/M +pitcher/M +pitchfork/MDSG +pitchman/M +pitchmen +piteous/YP +piteousness/M +pitfall/SM +pith/M +pithead/S +pithily +pithiness/M +pithy/RTP +pitiable +pitiably +pitiful/Y +pitiless/PY +pitilessness/M +piton/MS +pitta/S +pittance/MS +pitted +pitting +pituitary/SM +pity/GDSM +pitying/Y +pivot/MDGS +pivotal +pix/M +pixel/MS +pixie/MS +pizza/MS +pizzazz/M +pizzeria/SM +pizzicati +pizzicato/M +pj's +pk +pkg +pkt +pkwy +pl +placard/SMDG +placate/DSGN +placation/M +placatory +place's +place/AESDLG +placebo/SM +placed/U +placeholder/MS +placekick/MDRZGS +placekicker/M +placement/EASM +placenta/SM +placental/S +placer/SM +placid/Y +placidity/M +placings +placket/SM +plagiarism/SM +plagiarist/SM +plagiarize/DRSZG +plagiarizer/M +plagiary/M +plague/DSMG +plaice +plaid/MS +plain/MRYTSP +plainchant +plainclothes +plainclothesman/M +plainclothesmen +plainness/M +plainsman/M +plainsmen +plainsong/M +plainspoken +plaint/SMV +plaintiff/SM +plaintive/Y +plait/MDGS +plan/ZMRS +planar +plane's +plane/CGDS +planeload/MS +planer/M +planet/SM +planetarium/SM +planetary +plangency/M +plangent +plank/MDGS +planking/M +plankton/M +planned/U +planner/SM +planning/S +plant/MDRZGSJ +plantain/SM +plantar +plantation/MS +planter/M +planting/M +plantlike +plaque/SM +plash/MDSG +plasma/M +plasmon +plaster/SZGMDR +plasterboard/M +plasterer/M +plastic/SM +plasticity/M +plasticize/DSG +plastique +plat/XGMDNS +plate/MS +plateau/SMDG +plateful/SM +platelet/SM +platen/M +platform/SGMD +plating/M +platinum/M +platitude/SM +platitudinous +platonic +platoon/SGMD +platted +platter/SM +platting +platy/M +platypus/MS +platys +plaudit/SM +plausibility/M +plausible +plausibly +play/AEGMDS +playable/EU +playact/SGD +playacting/M +playback/MS +playbill/MS +playbook/MS +playboy/SM +player/SM +playfellow/SM +playful/PY +playfulness/M +playgirl/MS +playgoer/MS +playground/SM +playgroup/S +playhouse/MS +playlist/MS +playmate/MS +playoff/SM +playpen/SM +playroom/SM +playschool/S +plaything/SM +playtime/M +playwright/SM +plaza/MS +plea/MS +plead/DRZGSJ +pleader/M +pleading/MY +pleasant/UTYP +pleasanter +pleasantness/UM +pleasantry/SM +please/EDSG +pleasing/YS +pleasurably +pleasure/MGDSB +pleasureful +pleat/MDGS +pleb/S +plebby +plebe/MS +plebeian/MS +plebiscite/MS +plectra +plectrum/MS +pledge/DSMG +plenary/SM +plenipotentiary/SM +plenitude/SM +plenteous +plentiful/Y +plenty/M +plenum/S +pleonasm/MS +plethora/M +pleura/M +pleurae +pleurisy/M +plexus/MS +pliability/M +pliable +pliancy/M +pliant/Y +pliers/M +plight/SMDG +plimsoll/S +plinth/M +plinths +plod/S +plodded +plodder/MS +plodding/S +plonk/DRSZG +plop/MS +plopped +plopping +plosive/S +plot/MS +plotted +plotter/SM +plotting +plover/SM +plow/GMDS +plowman/M +plowmen +plowshare/MS +ploy's +ploy/S +pluck/MDSG +pluckily +pluckiness/M +plucky/RPT +plug's +plug/US +plugged/U +plugging/U +plughole/S +plugin/SM +plum/GMDS +plumage/M +plumb/MDRSZGJ +plumbed/U +plumber/M +plumbing/M +plume/MS +plummet/SGMD +plummy +plump/MDRYSTGP +plumpness/M +plumy/RT +plunder/SZGMDR +plunderer/M +plunge/DRSMZG +plunger/M +plunk/MDSG +pluperfect/SM +plural/SM +pluralism/M +pluralist/MS +pluralistic +plurality/SM +pluralization/M +pluralize/GDS +plus/MS +plush/MRYTP +plushness/M +plushy/RT +plutocracy/SM +plutocrat/SM +plutocratic +plutonium/M +pluvial +ply/AGDSM +plywood/M +pm +pneumatic +pneumatically +pneumococcal +pneumococci +pneumococcus +pneumonia/M +poach/DRSZG +poacher/M +poaching/M +pock/GMDS +pocket/SMDG +pocketbook/SM +pocketful/SM +pocketknife/M +pocketknives +pockmark/MDGS +pod/SM +podcast/SMG +podded +podding +podiatrist/SM +podiatry/M +podium/SM +poem/MS +poesy/M +poet/MS +poetaster/MS +poetess/MS +poetic/S +poetical/Y +poetry/M +pogrom/SM +poi/M +poignancy/M +poignant/Y +poinciana/SM +poinsettia/SM +point/MDRSZG +pointblank +pointed/Y +pointer/M +pointillism/M +pointillist/SM +pointless/PY +pointlessness/M +pointy/TR +poise/MGDS +poison/SJZGMDR +poisoner/M +poisoning/M +poisonous/Y +poke/MZGDRS +poker/M +pokey/MS +poky/TR +pol/SGMD +polar +polarity/SM +polarization/CM +polarize/CDSG +pole/MS +poleaxe/GDS +polecat/MS +polemic/MS +polemical/Y +polemicist/SM +polemics/M +polestar/SM +police/DSMG +policeman/M +policemen +policewoman/M +policewomen +policy/SM +policyholder/MS +policymaker/S +polio/MS +poliomyelitis/M +polish/ZGMDRS +polished/U +polisher/M +politburo/MS +polite/RYTP +politeness/M +politesse/M +politic/S +political/Y +politician/SM +politicization/M +politicize/CDSG +politicking/M +politico/SM +politics/M +polity/SM +polka/MDSG +poll/GMDNS +pollack/MS +pollard/S +pollen/M +pollinate/GNDS +pollination/M +pollinator/SM +polling/M +polliwog/SM +pollster/SM +pollutant/MS +pollute/ZGNDRS +polluted/U +polluter/M +pollution/M +polo/M +polonaise/SM +polonium/M +poltergeist/MS +poltroon/SM +poly +polyacrylamide +polyamory/S +polyandrous +polyandry/M +polyclinic/SM +polyester/MS +polyethylene/M +polygamist/MS +polygamous +polygamy/M +polyglot/SM +polygon/SM +polygonal +polygraph/GMD +polygraphs +polyhedral +polyhedron/SM +polymath/M +polymaths +polymer/SM +polymeric +polymerization/M +polymerize/GDS +polymorphic +polymorphous +polynomial/MS +polyp/MS +polyphonic +polyphony/M +polypropylene/M +polys +polysemous +polystyrene/M +polysyllabic +polysyllable/MS +polytechnic/MS +polytheism/M +polytheist/SM +polytheistic +polythene +polyunsaturate/DS +polyurethane/MS +polyvinyl +pom/S +pomade/DSMG +pomander/SM +pomegranate/MS +pommel/SGMD +pommy/S +pomp/M +pompadour/SMD +pompano/MS +pompom/SM +pomposity/M +pompous/YP +pompousness/M +ponce/GDS +poncho/SM +poncy +pond/MS +ponder/SZGDR +ponderer/M +ponderous/YP +ponderousness/M +pone/MS +pong/GDS +pongee/M +poniard/MS +pontiff/SM +pontifical/Y +pontificate/DSMG +pontoon/SM +pony/GDSM +ponytail/MS +poo/SGD +pooch/MDSG +poodle/SM +poof/MS +poofter/S +pooh/GMD +poohs +pool/GMDS +poolroom/MS +poolside/S +poop/GMDS +poor/TRYP +poorboy/M +poorhouse/SM +poorness/M +pop/SM +popcorn/M +pope/MS +popgun/SM +popinjay/MS +poplar/SM +poplin/M +popover/SM +poppa/MS +poppadom/S +popped +popper/SM +poppet/S +popping +poppy/SM +poppycock/M +populace/MS +popular/Y +popularity/UM +popularization/M +popularize/DSG +populate/ACGDS +populated/U +population/CM +populations +populism/M +populist/MS +populous/P +populousness/M +popup/MS +porcelain/SM +porch/MS +porcine +porcupine/SM +pore/MGDS +porgy/SM +pork/ZMR +porker/M +porky/RSMT +porn/M +porno/M +pornographer/MS +pornographic +pornographically +pornography/M +porosity/M +porous/P +porousness/M +porphyritic +porphyry/M +porpoise/MGDS +porridge/M +porringer/SM +port's/A +port/CAEGDS +portability/M +portable/MS +portage/DSMG +portal/SM +portcullis/MS +portend/SGD +portent/SM +portentous/YP +porter/ASM +porterhouse/SM +portfolio/MS +porthole/MS +portico/M +porticoes +portiere/MS +portion/KSGMD +portliness/M +portly/RPT +portmanteau/MS +portrait/MS +portraitist/SM +portraiture/M +portray/SGD +portrayal/MS +portulaca/M +pose's/A +pose/CAKEGDS +poser/EKSM +poseur/SM +posh/TR +posit/DSGV +position/CKEMS +positional/KE +positioned/K +positioning/AK +positive/MYPS +positiveness/M +positivism +positivist/S +positron/MS +poss +posse/MS +possess/AEVGSD +possession/ASM +possessive/SMYP +possessiveness/M +possessor/SM +possibility/SM +possible/SM +possibly +possum/SM +post/ZGMDRSJ +postage/M +postal +postbag/S +postbox/S +postcard/SM +postcode/S +postcolonial +postconsonantal +postdate/DSG +postdoc/MS +postdoctoral +poster/M +posterior/SM +posterity/M +postgraduate/SM +posthaste +posthumous/Y +posthypnotic +postie/S +postilion/SM +postindustrial +posting/M +postlude/SM +postman/M +postmark/SMDG +postmaster/MS +postmen +postmenopausal +postmeridian +postmistress/MS +postmodern +postmodernism/M +postmodernist/MS +postmortem/SM +postnasal +postnatal +postoperative +postpaid +postpartum +postpone/DSGL +postponement/SM +postprandial +postscript/SM +postseason/SM +postsynaptic +postulate/XDSMGN +postulation/M +postural +posture/MGJDS +posturing/M +postwar +postwoman +postwomen +posy/SM +pot/CSM +potability/M +potable/SM +potash/M +potassium/M +potato/M +potatoes +potbelly/DSM +potboiler/SM +potency/M +potent/Y +potentate/MS +potential/MYS +potentiality/SM +potentiate/GDS +potful/SM +pothead/SM +pother/SMDG +potherb/SM +potholder/MS +pothole/DRSMZG +pothook/SM +potion/SM +potluck/MS +potpie/SM +potpourri/SM +potsherd/SM +potshot/MS +pottage/M +potted +potter/GSMD +pottery/SM +potting +potty/PRSMT +pouch/MDSG +pouf/S +pouffe/S +poulterer/MS +poultice/DSMG +poultry/M +pounce/DSMG +pound's +pound/KDSG +poundage/M +pounding/SM +pour/GDSJ +pout/ZGMDRS +pouter/M +poverty/M +pow +powder/GSMD +powdery +power/MDSG +powerboat/MS +powerful/Y +powerhouse/SM +powerless/PY +powerlessness/M +powwow/SGMD +pox/MS +pp +ppm +ppr +pr +practicability/M +practicably +practical/SMY +practicality/SM +practice/DSMGB +practiced/U +practicum/SM +practitioner/SM +praetor/SM +praetorian +pragmatic/MS +pragmatical/Y +pragmatism/M +pragmatist/MS +prairie/SM +praise/EDSMG +praiseworthiness/M +praiseworthy/P +praline/SM +pram/MS +prance/DRSMZG +prancer/M +prancing/Y +prang/DSG +prank/MS +prankster/SM +praseodymium/M +prat/S +prate/MZGDRS +prater/M +pratfall/SM +prattle/DRSMZG +prattler/M +prawn/MDSG +pray/ZGDRS +prayer/M +prayerful/Y +preach/DRSZGL +preacher/M +preachment/M +preachy/RT +preadolescence/SM +preadolescent +preamble/MGDS +prearrange/LGDS +prearrangement/M +preassigned +precancel/SMDG +precancerous +precarious/PY +precariousness/M +precast +precaution/MS +precautionary +precede/DSG +precedence/M +precedent/SM +precept/SM +preceptor/SM +precinct/MS +preciosity/M +precious/YP +preciousness/M +precipice/SM +precipitant/MS +precipitate/XMYGNDS +precipitation/M +precipitous/Y +precis/M +precise/DRSYTGNP +preciseness/M +precision/M +preclude/GDS +preclusion/M +precocious/YP +precociousness/M +precocity/M +precognition/M +precognitive +precolonial +preconceive/GDS +preconception/SM +precondition/MDGS +precook/GSD +precursor/SM +precursory +predate/DSG +predator/MS +predatory +predawn +predecease/GDS +predecessor/SM +predefined +predesignate/GDS +predestination/M +predestine/DSG +predetermination/M +predetermine/ZGDRS +predeterminer/M +predicable +predicament/MS +predicate/MGNVDS +predication/M +predicative/Y +predict/BGVSD +predictability/UM +predictable/U +predictably/U +prediction/SM +predictor/MS +predigest/GDS +predilection/SM +predispose/GDS +predisposition/MS +prednisone +predominance/M +predominant/Y +predominate/YGDS +preemie/SM +preeminence/M +preeminent/Y +preempt/GVSD +preemption/M +preemptive/Y +preen/DSG +preexist/DGS +preexistence/M +pref +prefab/SM +prefabbed +prefabbing +prefabricate/DSGN +prefabrication/M +preface/DSMG +prefatory +prefect/SM +prefecture/MS +prefer/SBL +preferably +preference/MS +preferential/Y +preferment/M +preferred +preferring +prefigure/GDS +prefix/MDSG +preform/GSD +prefrontal +pregame/SM +pregnancy/SM +pregnant +preheat/GSD +prehensile +prehistorian/S +prehistoric +prehistorical/Y +prehistory/M +prehuman +preinstalled +prejudge/GDS +prejudgment/SM +prejudice/MGDS +prejudiced/U +prejudicial +prekindergarten/SM +prelacy/M +prelate/SM +prelim/SM +preliminary/SM +preliterate +prelude/MS +premarital +premature/Y +premed/SM +premedical +premeditate/DSGN +premeditated/U +premeditation/M +premenstrual +premier/SGMD +premiere/MS +premiership/MS +premise/DSMG +premium/SM +premix/GDS +premolar/SM +premonition/MS +premonitory +prenatal/Y +prenup/SM +prenuptial +preoccupation/SM +preoccupy/DSG +preoperative +preordain/GDS +preowned +prep/MS +prepackage/DSG +prepacked +prepaid +preparation/SM +preparatory +prepare/GDS +prepared/UP +preparedness/UM +prepay/GSL +prepayment/MS +prepend +preponderance/SM +preponderant/Y +preponderate/GDS +preposition/SM +prepositional/Y +prepossess/GDS +prepossessing/U +prepossession/SM +preposterous/Y +prepped +prepping +preppy/TRSM +prepubescence/M +prepubescent/SM +prepuce/MS +prequel/MS +prerecord/GSD +preregister/SGD +preregistration/M +prerequisite/MS +prerogative/SM +pres +presage/MGDS +presbyopia/M +presbyter/SM +presbytery/SM +preschool/SZMR +preschooler/M +prescience/M +prescient/Y +prescribe/DSG +prescript/SVM +prescription/SM +prescriptive/Y +preseason/SM +presence/SM +present/LMDRYZGSB +presentably +presentation/ASM +presenter/M +presentiment/SM +presentment/SM +preservation/M +preservationist/SM +preservative/SM +preserve/BDRSMZG +preserver/M +preset/S +presetting +preshrank +preshrink/GS +preshrunk +preside/GDS +presidency/SM +president/MS +presidential +presidium/M +presort/DGS +press's +press/ACGSD +pressed/U +presser/MS +pressie/S +pressing/SMY +pressman/M +pressmen +pressure/DSMG +pressurization/M +pressurize/CGDS +pressurizer/SM +prestidigitation/M +prestige/M +prestigious +presto/SM +presumably +presume/GDSB +presumption/SM +presumptive +presumptuous/YP +presumptuousness/M +presuppose/DSG +presupposition/MS +pretax +preteen/MS +pretend/DRZGS +pretender/M +pretense/SXMN +pretension/M +pretentious/UY +pretentiousness/M +preterit/SM +preterm +preternatural/Y +pretest/DGS +pretext/MS +pretrial/S +prettify/GDS +prettily +prettiness/M +pretty/TGDRSMP +pretzel/MS +prevail/DGS +prevalence/M +prevalent +prevaricate/DSGNX +prevarication/M +prevaricator/SM +prevent/DBSGV +preventable/U +preventative/MS +prevention/M +preventive/SM +preview/MDRSZG +previous/Y +prevision/MS +prewar +prey/GMDS +prezzie/S +priapic +price's +price/AGDS +priceless +pricey +pricier +priciest +prick/MDRYSZG +pricker/M +prickle/MGDS +prickliness/M +prickly/PRT +pride/MGDS +prideful/Y +prier/M +priest/SMY +priestess/MS +priesthood/SM +priestliness/M +priestly/RTP +prig/MS +priggish/P +priggishness/M +prim/ZGDRYP +primacy/M +primal +primarily +primary/SM +primate/MS +prime/MS +primer/M +primeval +priming/M +primitive/SPMY +primitiveness/M +primmer +primmest +primness/M +primogenitor/SM +primogeniture/M +primordial/Y +primp/DSG +primrose/SM +primula/S +prince/SMY +princedom/SM +princeliness/M +princely/PRT +princess/MS +principal/SMY +principality/SM +principle/DSM +principled/U +print/AMDSG +printable/U +printer/MS +printing/SM +printmaking +printout/SM +prion/S +prior/MS +prioress/MS +prioritization +prioritize/DSG +priority/SM +priory/SM +prism/MS +prismatic +prison/SZMR +prisoner/M +prissily +prissiness/M +prissy/PTR +pristine +prithee +privacy/M +private/XMYTNRS +privateer/SM +privation/CSM +privatization/SM +privatize/DSG +privet/SM +privilege/DSMG +privileged/U +privily +privy/RSMT +prize/MGDS +prized/A +prizefight/ZGSMR +prizefighter/M +prizefighting/M +prizewinner/MS +prizewinning +pro/SM +probabilistic +probability/SM +probable/SM +probably +probate/MN +probation/ZMR +probational +probationary +probationer/M +probe/MGDSBJ +probity/M +problem/MS +problematic/U +problematical/Y +probosces +proboscis/MS +procaine/M +procedural +procedure/SM +proceed/GJDS +proceeding/M +proceeds/M +process's +process/AGDS +processable +processed/U +procession/GD +processional/MS +processor/SM +proclamation/MS +proclivity/SM +procrastinate/DSGN +procrastination/M +procrastinator/MS +procreate/V +proctor/GMDS +procurement/M +prod/MS +prodigal/MYS +prodigality/M +prodigious/Y +prodigy/SM +produce's +produce/AZGDRS +producer/AM +producible/A +production/ASM +productive/UY +productiveness/M +productivity/M +prof/MS +profanation/MS +profane/PYGDS +profaneness/M +profanity/SM +professed/Y +profession/SM +professional/MYS +professionalism/M +professionalization +professionalize/DSG +professor/SM +professorial/Y +professorship/SM +proffer/GMDS +proficiency/M +proficient/MYS +profit/BGD +profitability/M +profitable/U +profitably/U +profiteer/MDGS +profiteering/M +profiterole/SM +profitless +profligacy/M +profligate/SMY +proforma +profound/RYTP +profoundness/M +profundity/SM +profuse/PY +profuseness/M +progenitor/SM +progeny/M +progesterone/M +progestin/S +prognathous +prognoses +prognosis/M +prognostic/MS +prognosticate/XGNDS +prognostication/M +prognosticator/MS +program/CAS +programmable/MS +programmatic +programmed/AC +programmer/MS +programming/SM +progress/MDSGV +progression/MS +progressive/PMYS +progressiveness/M +prohibit/DGVS +prohibition/SM +prohibitionist/MS +prohibitive/Y +prohibitory +project/GMDS +projectile/SM +projection/SM +projectionist/SM +projector/MS +prokaryote/MS +prokaryotic +prole/S +proletarian/MS +proletariat/M +proliferate/DSGN +proliferation/M +prolific +prolifically +prolix/Y +prolixity/M +prologue/SM +prolongation/SM +prom/M +promenade/MGDS +promethium/M +prominence/M +prominent/Y +promiscuity/M +promiscuous/Y +promise/DSMG +promising/Y +promissory +promo/M +promontory/SM +promote/DRZG +promoter/M +promotional +prompt/JPSMDRYZTG +prompted/U +prompter/M +prompting/M +promptitude/M +promptness/M +promulgate/GNDS +promulgation/M +promulgator/MS +prone/P +proneness/M +prong/MDS +pronghorn/MS +pronominal/M +pronounce/DSLG +pronounceable/U +pronouncement/SM +pronto +pronunciation/MS +proof/ADGSM +proofread/SRZG +proofreader/M +prop/MS +propaganda/M +propagandist/MS +propagandize/GDS +propagate/DSGN +propagation/M +propagator/SM +propel/S +propellant/MS +propelled +propeller/SM +propelling +propensity/SM +proper/MRYT +property/DSM +prophecy/SM +prophesier/M +prophesy/DRSMZG +prophet/SM +prophetess/MS +prophetic +prophetical/Y +prophylactic/SM +prophylaxes +prophylaxis/M +propinquity/M +propitiate/DSGN +propitiation/M +propitiatory +propitious/Y +proponent/SM +proportion/ESM +proportional/YS +proportionality +proportionate/EY +proposal/MS +propped +propping +propranolol +proprietary/SM +proprieties/M +proprietor/SM +proprietorial/Y +proprietorship/M +proprietress/MS +propriety/SM +propulsion/M +propulsive +prorate/DSG +prorogation/M +prorogue/GD +prosaic +prosaically +proscenium/SM +prosciutto/M +proscribe/DG +proscription/MS +prose/M +prosecute/DSXGN +prosecution/M +prosecutor/MS +proselyte/DSMG +proselytism/M +proselytize/DRSZG +proselytizer/M +prosocial +prosody/SM +prospect/MDGVS +prospective/Y +prospector/SM +prospectus/MS +prosper/GSD +prosperity/M +prosperous/Y +prostate/MS +prostheses +prosthesis/M +prosthetic +prostitute/MGNDS +prostitution/M +prostrate/GNXDS +prostration/M +prosy/RT +protactinium/M +protagonist/SM +protean +protect/GVSD +protected/U +protection/SM +protectionism/M +protectionist/MS +protective/PY +protectiveness/M +protector/MS +protectorate/MS +protege/SM +protegee/S +protein/SM +protestant/S +protestation/MS +protocol/MS +proton/SM +protoplasm/M +protoplasmic +prototype/MGS +prototypical +protozoa +protozoan/MS +protozoic +protract/GD +protrude/GDS +protrusile +protrusion/MS +protuberance/MS +protuberant +proud/RYT +prov/NB +provability/M +provably +prove/EAGDS +proved/U +proven/U +provenance/SM +provender/M +provenience/M +proverbial/Y +provide/DRSZG +provided/U +providence/M +provident/Y +providential/Y +provider/M +province/MS +provincial/SMY +provincialism/M +provisional/Y +proviso/SM +provocateur/S +provocative/PY +provocativeness/M +provoke/DRSZG +provoked/U +provoker/M +provoking/Y +provolone/M +provost/SM +prow/MS +prowess/M +prowl/MDRSZG +prowler/M +proximal +proximate +proximity/M +proxy/SM +prude/MS +prudence/M +prudent/Y +prudential/Y +prudery/M +prudish/YP +prudishness/M +prune/MZGDRS +pruner/M +prurience/M +prurient/Y +pry/ZTGDRSM +psalm/MS +psalmist/SM +psaltery/SM +psephologist/S +psephology +pseud/S +pseudo/S +pseudonym/SM +pseudonymous +pseudoscience/MS +pseudy +pshaw/MS +psi/SM +psittacosis/M +psoriasis/M +psst +psych/MDSG +psyche/M +psychedelia +psychedelic/SM +psychedelically +psychiatric +psychiatrist/SM +psychiatry/M +psychic/MS +psychical/Y +psycho/SM +psychoactive +psychoanalyses +psychoanalysis/M +psychoanalyst/SM +psychoanalytic +psychoanalytical/Y +psychoanalyze/DSG +psychobabble/M +psychodrama/MS +psychogenic +psychokinesis +psychokinetic +psychological/Y +psychologist/MS +psychology/SM +psychometric +psychoneuroses +psychoneurosis/M +psychopath/M +psychopathic +psychopathology +psychopaths +psychopathy/M +psychopharmacology +psychophysiology +psychos/S +psychosis/M +psychosomatic +psychotherapist/MS +psychotherapy/SM +psychotic/SM +psychotically +psychotropic/MS +psychs +pt/C +ptarmigan/MS +pterodactyl/MS +ptomaine/SM +pub/SM +pubertal +puberty/M +pubes/M +pubescence/M +pubescent +pubic +pubis/M +public/AM +publican/AMS +publication/ASM +publicist/MS +publicity/M +publicize/GDS +publicly +publish/AGDS +publishable +published/U +publisher/MS +publishing/M +puce/M +puck/ZMRS +pucker/MDG +puckish/YP +puckishness/M +pud/S +pudding/SM +puddle/DSMG +puddling/M +pudenda +pudendum/M +pudginess/M +pudgy/PRT +pueblo/SM +puerile +puerility/M +puerperal +puff/ZGMDRS +puffball/SM +puffer/M +puffin/SM +puffiness/M +puffy/PRT +pug/SM +pugilism/M +pugilist/SM +pugilistic +pugnacious/YP +pugnaciousness/M +pugnacity/M +puke/MGDS +pukka +pulchritude/M +pulchritudinous +pule/GDS +pull/ZGMDRS +pullback/MS +puller/M +pullet/SM +pulley/SM +pullout/MS +pullover/SM +pulmonary +pulp/GMDS +pulpiness/M +pulpit/SM +pulpwood/M +pulpy/RPT +pulsar/SM +pulsate/XGNDS +pulsation/M +pulse/AMGDS +pulverization/M +pulverize/DSG +puma/MS +pumice/SM +pummel/SGD +pump/ZGMDRS +pumper/M +pumpernickel/M +pumpkin/MS +pun/SM +punch/MDRSZG +punchbag/S +puncheon/MS +puncher/M +punchline/S +punchy/TR +punctilio/M +punctilious/PY +punctiliousness/M +punctual/Y +punctuality/M +punctuate/GNDS +punctuation/M +puncture/DSMG +pundit/SM +punditry/M +pungency/M +pungent/Y +puniness/M +punish/BLGDS +punished/U +punishing/Y +punishment/MS +punitive/Y +punk/TMRS +punned +punnet/S +punning +punster/SM +punt/ZGMDRS +punter/M +puny/TRP +pup/SM +pupa/M +pupae +pupal +pupate/DSG +pupil/MS +pupped +puppet/MS +puppeteer/SM +puppetry/M +pupping +puppy/SM +purblind +purchase/DRSMZGB +purchaser/M +purdah/M +pure/PYTR +purebred/SM +puree/MDS +pureeing +pureness/M +purgative/SM +purgatorial +purgatory/SM +purge/MZGDRS +purger/M +purification/M +purifier/M +purify/NDRSZG +purine/MS +purism/M +purist/MS +puristic +puritan/SM +puritanical/Y +puritanism/M +purity/M +purl/GMDS +purlieu/SM +purloin/SGD +purple/MTRS +purplish +purport/SMDG +purported/Y +purpose/DSMYG +purposed/A +purposeful/YP +purposefulness/M +purposeless/PY +purr/GMDS +purse/MZGDRS +purser/M +pursuance/M +pursuant +pursue/ZGDRS +pursuer/M +pursuit/SM +purulence/M +purulent +purvey/DSG +purveyance/M +purveyor/SM +purview/M +pus/M +push/ZGMDRS +pushbike/S +pushcart/SM +pushchair/S +pusher/M +pushily +pushiness/M +pushover/MS +pushpin/S +pushy/TRP +pusillanimity/M +pusillanimous/Y +puss/MS +pussy/TRSM +pussycat/MS +pussyfoot/DSG +pustular +pustule/SM +put/ISM +putative +putout/MS +putrefaction/M +putrefactive +putrefy/GDS +putrescence/M +putrescent +putrid +putsch/MS +putt/ZGMDRS +putted/I +puttee/MS +putter/MDRZG +putterer/M +putting/I +putty/GDSM +putz/S +puzzle/MZGDRSL +puzzlement/M +puzzler/M +pvt +pwn/SGD +pyelonephritis +pygmy/SM +pylon/SM +pylori +pyloric +pylorus/M +pyorrhea/M +pyramid/GSMD +pyramidal +pyre/MS +pyrimidine/MS +pyrite/SM +pyrites/M +pyromania/M +pyromaniac/SM +pyrotechnic/S +pyrotechnical +pyrotechnics/M +pyruvate +python/SM +pyx/MS +pzazz +q +qr +qt/S +qty +qua +quack/GMDS +quackery/M +quad/MS +quadrangle/SM +quadrangular +quadrant/MS +quadraphonic +quadratic/MS +quadrature +quadrennial +quadrennium/MS +quadriceps/MS +quadrilateral/SM +quadrille/XMNS +quadrillion/M +quadriplegia/M +quadriplegic/SM +quadrivium/M +quadruped/MS +quadrupedal +quadruple/MGDS +quadruplet/MS +quadruplicate/MGNDS +quadruplication/M +quaff/GMDS +quagmire/SM +quahog/MS +quail/GMDS +quaint/PRYT +quaintness/M +quake/MGDS +quaky +qualification/EM +qualified/U +qualifier/SM +qualify/EGXNDS +qualitative/Y +quality/SM +qualm/MS +qualmish +quandary/SM +quango/S +quanta +quantifiable +quantification/M +quantifier/M +quantify/NDRSZG +quantitation +quantitative/Y +quantity/SM +quantization +quantize +quantum/M +quarantine/MGDS +quark/MS +quarrel/SZGMDR +quarreler/M +quarrelsome/P +quarrelsomeness/M +quarry/DSMG +quart/MS +quarter/SGMDY +quarterback/GMDS +quarterdeck/MS +quarterfinal/SM +quarterly/SM +quartermaster/MS +quarterstaff/M +quarterstaves +quartet/SM +quarto/MS +quartz/M +quasar/MS +quash/GDS +quasi +quatrain/MS +quaver/MDSG +quavery +quay/MS +quayside/S +queasily +queasiness/M +queasy/TPR +queen/GMDYS +queenly/RT +queer/PTGMDRYS +queerness/M +quell/GDS +quench/ZGDRSB +quenchable/U +quencher/M +quenchless +querulous/YP +querulousness/M +query/DSMG +ques +quesadilla/MS +quest/IFAMS +quested +questing +question/SMDRZGBJ +questionable/U +questionably/U +questioned/U +questioner/M +questioning/MY +questionnaire/SM +queue's +queue/CDS +queuing +quibble/DRSMZG +quibbler/M +quiche/SM +quick/MNRYXTP +quicken/DG +quickfire +quickie/SM +quicklime/M +quickness/M +quicksand/MS +quicksilver/M +quickstep/MS +quid/MS +quiescence/M +quiescent/Y +quiet/SMDNRYXTGP +quieten/DG +quietism +quietness/M +quietude/IEM +quietus/MS +quiff/S +quill/SM +quilt/SMDRZG +quilter/M +quilting/M +quin/S +quince/SM +quine/S +quinidine +quinine/M +quinoa +quinsy/M +quint/SM +quintessence/SM +quintessential/Y +quintet/SM +quintuple/MGDS +quintuplet/MS +quip/MS +quipped +quipping +quipster/SM +quire's +quire/IAS +quirk/SMDG +quirkiness/M +quirky/RTP +quirt/SM +quisling/SM +quit/S +quitclaim/MS +quite +quittance/M +quitter/SM +quitting +quiver/SMDG +quivery +quixotic +quixotically +quiz/M +quizzed +quizzer/SM +quizzes +quizzical/Y +quizzing +quo/H +quoin/SM +quoit/SMDG +quondam +quorate/I +quorum/SM +quot/B +quota/SM +quotability/M +quotation/SM +quote's +quote/UDSG +quotidian +quotient/SM +qwerty +r/S +rabbet/GMDS +rabbi/SM +rabbinate/M +rabbinic +rabbinical +rabbit/GMDS +rabble/MS +rabid/PY +rabidness/M +rabies/M +raccoon/MS +race/MZGDRS +racecourse/SM +racegoer/S +racehorse/MS +raceme/MS +racer/M +racetrack/MS +raceway/MS +racial/Y +racialism/M +racialist/MS +racily +raciness/M +racing/M +racism/M +racist/SM +rack/GMDS +racket/SMDG +racketeer/SMDG +racketeering/M +raconteur/SM +racquetball/SM +racy/PRT +rad/SM +radar/SM +radarscope/SM +raddled +radial/SMY +radian/S +radiance/M +radiant/Y +radiate/DSGNX +radiation/M +radiator/SM +radical/SMY +radicalism/M +radicalization/M +radicalize/DSG +radicchio/M +radii +radio/MDGS +radioactive/Y +radioactivity/M +radiocarbon/M +radiogram/MS +radiographer/SM +radiography/M +radioisotope/MS +radiologist/SM +radiology/M +radioman/M +radiomen +radiometer/MS +radiometric +radiometry/M +radiophone/SM +radioscopy/M +radiosonde/SM +radiosurgery +radiotelegraph/M +radiotelegraphs +radiotelegraphy/M +radiotelephone/MS +radiotherapist/MS +radiotherapy/M +radish/MS +radium/M +radius/M +radon/M +raffia/M +raffish/YP +raffishness/M +raffle/DSMG +raft/ZGMDRS +rafter/M +rafting/M +rag/SGMD +raga/MS +ragamuffin/MS +ragbag/M +rage/MS +ragga +ragged/RYTP +raggedness/M +raggedy/RT +ragging +raging/Y +raglan/SM +ragout/SM +ragtag/S +ragtime/M +ragweed/M +ragwort +rah +raid/ZGMDRS +raider/M +rail's +rail/CGDS +railcard/S +railing/SM +raillery/SM +railroad/SZGMDR +railroader/M +railroading/M +railway/SM +railwayman +railwaymen +raiment/M +rain/GMDS +rainbow/SM +raincoat/SM +raindrop/SM +rainfall/SM +rainmaker/SM +rainmaking/M +rainproof +rainstorm/MS +rainwater/M +rainy/RT +raise/MZGDRS +raiser/M +raisin/SM +rajah/M +rajahs +rake/MGDS +rakish/YP +rakishness/M +rally/DSMG +ram/SM +ramble/DRSMZGJ +rambler/M +rambunctious/PY +rambunctiousness/M +ramekin/SM +ramie/M +ramification/M +ramify/DSXNG +ramjet/SM +rammed +ramming +ramp/GMS +rampage/DSMG +rampancy/M +rampant/Y +rampart/SM +ramrod/SM +ramrodded +ramrodding +ramshackle +ran/A +ranch/MDRSZG +rancher/M +ranching/M +rancid/P +rancidity/M +rancidness/M +rancor/M +rancorous/Y +rand/M +randiness/M +random/PSY +randomization/M +randomize/DSG +randomness/MS +randy/RTP +ranee/MS +rang/ZR +range's +range/CGDS +rangefinder/S +ranger/M +ranginess/M +rangy/RTP +rank/TGJPMDRYS +ranking/M +rankle/DSG +rankness/M +ransack/SGD +ransom/SZGMDR +ransomer/M +ransomware +rant/ZGMDJRS +ranter/M +rap/SZGMDR +rapacious/PY +rapaciousness/M +rapacity/M +rape/MS +raper/M +rapeseed/M +rapid/PMRYTS +rapidity/M +rapidness/M +rapier/SM +rapine/M +rapist/SM +rapped +rappel/SM +rappelled +rappelling +rapper/SM +rapping +rapport/MS +rapporteur/S +rapprochement/SM +rapscallion/MS +rapt/YP +raptness/M +raptor/S +rapture/MS +rapturous/Y +rare/YTGPDRS +rarebit/MS +rarefaction/M +rarefy/GDS +rareness/M +rarity/SM +rascal/SMY +rash/ZTMRSYP +rasher/M +rashness/M +rasp/GMDS +raspberry/SM +raspy/RT +raster +rat/SM +ratatouille/M +ratbag/S +ratchet/GMDS +rate/JXMZGNDRS +rated/U +ratepayer/S +rater/M +rather +rathskeller/SM +ratification/M +ratifier/M +ratify/NDRSZG +rating/M +ratio/MS +ratiocinate/GNDS +ratiocination/M +ration/MDG +rational/SMY +rationale/MS +rationalism/M +rationalist/SM +rationalistic +rationality/M +rationalization/MS +rationalize/DSG +ratlike +ratline/SM +rattan/SM +ratted +ratter/SM +ratting +rattle/DRSMZGJ +rattlebrain/SMD +rattler/M +rattlesnake/SM +rattletrap/SM +rattly +rattrap/SM +ratty/RT +raucous/YP +raucousness/M +raunchily +raunchiness/M +raunchy/TRP +ravage/DRSMZG +ravager/M +ravages/M +rave/JMZGDRS +ravel's +ravel/UDSG +raveling/S +raven/MDSG +ravenous/Y +ravine/SM +raving/M +ravioli/SM +ravish/DRSZGL +ravisher/M +ravishing/Y +ravishment/M +raw/PTMR +rawboned +rawhide/M +rawness/M +ray/SM +rayon/M +raze/GDS +razor/MS +razorback/MS +razz/GMDS +razzmatazz/M +rcpt +rd +re/DSMYTGVJ +reach/MDSGB +reachable/U +reacquire/DSG +react/V +reactance +reactant/SM +reactionary/SM +reactivity +read/ZGMRBJS +readability/SM +reader/M +readership/SM +readily +readiness/M +reading/M +readmitted +readout/SM +ready/DRSTGP +reafforestation +real/TMRYPS +realism/M +realist/SM +realistic/U +realistically/U +realities +reality/UM +realization/MS +realize/DSBG +realized/U +realm/MS +realness/M +realpolitik/M +realty/M +ream/ZGMDRS +reamer/M +reap/ZGDRS +reaper/M +rear/GMDS +rearguard/MS +rearmost +rearward/S +reason/SMDRZGB +reasonable/UP +reasonableness/UM +reasonably/U +reasoner/M +reasoning/M +reassuring/Y +rebate/M +rebel/MS +rebellion/MS +rebellious/YP +rebelliousness/M +rebid/S +rebidding +rebirth/M +reboil/SDG +rebuild/SG +rebuke/DSMG +rebuking/Y +rebuttal/MS +rec'd +rec/M +recalcitrance/M +recalcitrant +recant/SDG +recantation/SM +recap/MS +recapitalization +recce/S +recd +receipt/SMDG +receivables/M +receive/DRSZGB +receiver/M +receivership/M +recent/RYTP +recentness/M +receptacle/SM +reception/MS +receptionist/SM +receptive/PY +receptiveness/M +receptivity/M +receptor/SM +recess/MDSGV +recessional/SM +recessionary +recessive/SM +recherche +recidivism/M +recidivist/SM +recipe/SM +recipient/SM +reciprocal/SMY +reciprocate/GNDS +reciprocation/M +reciprocity/M +recital/SM +recitalist/MS +recitative/MS +reciter/SM +reckless/YP +recklessness/M +reckon/SJDG +reckoning/M +reclamation/M +recline/DRSZG +recliner/M +recluse/SMV +recognizable/U +recognizably/U +recognize/DRSGB +recognized/U +recombination +recompense/DSMG +recompilation +recompile/GD +recon/S +reconcile/GDSB +reconciliation/S +recondite +reconfiguration +reconfigure/D +reconnaissance/MS +reconnoiter/DGS +reconstruct/V +reconstructed/U +recorded/U +recorder/MS +recording/MS +recoup/DG +recourse/M +recoverable/U +recovery/SM +recreant/MS +recreational +recriminate/DSGNX +recrimination/M +recriminatory +recrudesce/GDS +recrudescence/M +recrudescent +recruit/LSMDRZG +recruiter/M +recruitment/M +rectal/Y +rectangle/MS +rectangular +rectifiable +rectification/M +rectifier/M +rectify/XNDRSZG +rectilinear +rectitude/M +recto/MS +rector/SM +rectory/SM +rectum/SM +recumbent +recuperate/GNVDS +recuperation/M +recur/S +recurred +recurrence/SM +recurring +recursion/S +recuse/DSG +recyclable/SM +recycling/M +red/PSM +redact/SDG +redaction/M +redactor/SM +redbird/SM +redbreast/MS +redbrick +redcap/SM +redcoat/SM +redcurrant/S +redden/SDG +redder +reddest +reddish +redeem/RZB +redeemer/M +redemption/M +redemptive +redhead/SMD +redirection +redistrict/GD +redivide/GDS +redlining/M +redneck/SM +redness/M +redo/G +redolence/M +redolent +redoubt/SBM +redoubtably +redound/SDG +redraw/SG +redskin/SM +reduce/DRSZG +reducer/M +reducible +reductase/M +reduction/SM +reductionist +reductive +redundancy/SM +redundant/Y +redwood/SM +redye/DS +reediness/M +reedy/RTP +reef/ZGMDRS +reefer/M +reek/GMDS +reel's +reel/UGDS +reeve/G +reexport/SDG +ref/SZM +refashion/DGS +refection/M +refectory/SM +refer/B +referee/DSM +refereeing +reference/MGDS +referendum/MS +referent/SM +referential +referral/SM +referred +referrer/SM +referring +reffed +reffing +refill/BM +refined/U +refinement/SM +refiner/SM +refinery/S +refitting +reflate/XDSGN +reflationary +reflect/GVSD +reflection/MS +reflective/Y +reflectivity +reflector/MS +reflexive/SMY +reflexivity +reflexology +reforge/DSG +reform/MZ +reformat/V +reformatory/SM +reformatting +reformed/U +reformist/S +refortify/GDS +refract/SGVD +refraction/M +refractory/SM +refrain/SGMD +refresh/ZGLDRS +refresher/M +refreshing/Y +refreshment/SM +refreshments/M +refrigerant/SM +refrigerate/DSGN +refrigeration/M +refrigerator/MS +refuge/SM +refugee/SM +refulgence/M +refulgent +refund/B +refurbishment/MS +refusal/MS +refutation/MS +refute/BDRSZG +refuter/M +reg +regal/DYG +regalement/M +regalia/M +regard/ESMDG +regardless +regards/M +regather/DGS +regatta/SM +regency/SM +regeneracy/M +regenerate/V +regex/M +regexp/S +reggae/M +regicidal +regicide/MS +regime/SM +regimen/SM +regiment/MDGS +regimental +regimentation/M +region/SM +regional/Y +regionalism/MS +register/GMDS +registered/U +registrant/MS +registrar/MS +registration/SM +registry/SM +regnant +regress/MDSGV +regression/MS +regret/SM +regretful/Y +regrettable +regrettably +regretted +regretting +regrind/GS +reground +regroup/DGS +regular/MYS +regularity/SM +regularization/M +regularize/DSG +regulate/CDSGNV +regulated/U +regulation/CM +regulations +regulator/MS +regulatory +regurgitate/DSGN +regurgitation/M +rehab/MS +rehabbed +rehabbing +rehabilitate/GNVDS +rehabilitation/M +rehang/SDG +rehears/GD +rehearsal/MS +rehearsed/U +rehi +rehung +reify/NDSG +reign/MDSG +reimburse/BDSGL +reimbursement/MS +rein/GD +reindeer/M +reinforce/LGDS +reinforcement/SM +reinitialize +reinstall/DG +reinstatement/M +reinsurance +reiterate/V +reject/GSMD +rejection/SM +rejoice/JGDS +rejoicing/M +rejoinder/SM +rejuvenate/DSGN +rejuvenation/M +rel +relate/DRSBXZGNV +relatedness/M +relater/M +relation/M +relational +relationship/MS +relative/MYS +relativism/M +relativist/S +relativistic +relativity/M +relax/DRSZG +relaxant/MS +relaxation/SM +relaxer/M +relay/D +release/B +released/U +relegate/GNDS +relent/SGD +relentless/PY +relentlessness/M +relevance/M +relevancy/M +relevant/Y +reliability/UM +reliable/U +reliably/U +reliance/M +reliant +relic/MS +relief/SM +relieve/ZGDRS +reliever/M +religion/SM +religiosity +religious/MYP +religiousness/M +reline/DSG +relinquish/LDSG +relinquishment/M +reliquary/SM +relish/GMDS +relist/SGD +relocate/B +reluctance/M +reluctant/Y +rely/GDS +rem/M +remain/SGD +remainder/GMDS +remand/SGD +remapping +remark/B +remarkableness/M +remarkably +remarked/U +remediable +remedy/GDSM +remember/DG +remembered/U +remembrance/MS +reminder/M +reminisce/GDS +reminiscence/MS +reminiscent/Y +remiss/PY +remissness/M +remit/S +remittance/SM +remitted +remitting/U +remix/DSG +remnant/MS +remodel/GDS +remold/SGD +remonstrant/SM +remonstrate/DSG +remorse/M +remorseful/Y +remorseless/PY +remorselessness/M +remote/RSMYTP +remoteness/M +removal/SM +remunerate/GNVXDS +remuneration/M +renaissance/MS +renal +renascence/S +rend/GS +render/SGMDJ +rendering/M +rendezvous/GMDS +rendition/MS +renegade/DSMG +renege/DRSZG +reneger/M +renew/DSBG +renewal/MS +rennet/M +rennin/M +renounce/LDSG +renouncement/M +renovate/DSXGN +renovation/M +renovator/MS +renown/MD +rent/ZGMDRS +rental/SM +renter/M +renunciation/SM +reopen/SDG +reorg/MDSG +rep/SM +repaint/GDS +repair/BZR +repairer/M +repairman/M +repairmen +reparable +reparation/MS +reparations/M +repartee/M +repatriate/XDSMGN +repatriation/M +repeat/SMDRZGB +repeatability +repeatable/U +repeatably +repeated/Y +repeater/M +repeating/M +repel/S +repelled +repellent/SM +repelling +repent/SDG +repentance/M +repentant/Y +repercussion/S +repertoire/MS +repertory/SM +repetition/MS +repetitious/YP +repetitiousness/M +repetitive/YP +repetitiveness/M +rephotograph/DG +replaceable +replant/GSD +replenish/LGDS +replenishment/M +replete/PDSGN +repleteness/M +repletion/M +replica/SM +replicate/DSGNX +replication/M +replicator/S +reportage/M +reported/Y +reportorial +reposeful +reposition +repository/SM +reprehend/DGS +reprehensibility/M +reprehensible +reprehensibly +reprehension/M +represent/GDS +representational +representative/MS +represented/U +repression/MS +repressive/PY +reprieve/DSMG +reprimand/GSMD +reprisal/SM +reprise/SMG +reproach/GMDSB +reproachful/Y +reprobate/MS +reproductive +reprogramming +reproving/Y +reptile/SM +reptilian/MS +republic/S +republicanism/M +repudiate/XGNDS +repudiation/M +repudiator/MS +repugnance/M +repugnant +repulsion/M +repulsive/YP +repulsiveness/M +repurchase/GDS +reputability/M +reputably/E +reputation/MS +repute/DSMGB +reputed/Y +request/GDR +requiem/SM +require/LDG +requirement/MS +requisite/XMNS +requisition/GMD +requital/M +requite/DRSZG +requited/U +requiter/M +reread/SG +rerecord/GDS +rerunning +resample/GDS +resat +rescind/SDG +rescission/M +rescue/DRSMZG +rescuer/M +reseal/B +resemble/DSG +resend +resent/LSDG +resentful/YP +resentfulness/M +resentment/MS +reserpine/M +reservation/MS +reserved/UY +reservedness/M +reservist/SM +reservoir/SM +resetting +reshipping +residence/SM +residency/SM +resident/MS +residential +residua +residual/MS +residue/SM +residuum/M +resignation/SM +resigned/Y +resilience/M +resiliency/M +resilient/Y +resinous +resist/SMDRZG +resistance/SM +resistant/U +resistible +resistivity +resistless +resistor/MS +resit/S +resitting +resold +resole/DSG +resolute/PY +resoluteness/M +resolve/RBM +resolved/U +resonance/SM +resonant/Y +resonate/GDS +resonator/SM +resorption/M +resound/SGD +resounding/Y +resourceful/YP +resourcefulness/M +resp +respect/ESGVMD +respectability/M +respectable +respectably +respectful/EY +respectfulness/M +respective/Y +respell/SGD +respiration/M +respirator/SM +respiratory +respire/DG +resplendence/M +resplendent/Y +respond/SGD +respondent/SM +response/MS +responsibility/SM +responsible +responsibly +responsive/UYP +responsiveness/UM +rest/GVMDS +restate/GDS +restaurant/SM +restaurateur/MS +restful/YP +restfuller +restfullest +restfulness/M +restitution/M +restive/YP +restiveness/M +restless/PY +restlessness/M +restoration/SM +restorative/SM +restorer/SM +restrained/U +restraint/MS +restrict/SDGV +restricted/U +restriction/MS +restrictive/YP +restrictiveness/M +restring/SG +restroom/SM +restructuring/SM +result/GSMD +resultant/SM +resume/DSMG +resumption/MS +resupply/DSG +resurgence/MS +resurgent +resurrect/GSD +resurrection/MS +resuscitate/GNDS +resuscitation/M +resuscitator/SM +retailer/MS +retain/SDRZG +retainer/M +retake/G +retaliate/DSGNVX +retaliation/M +retaliatory +retard/SMDRZG +retardant/SM +retardation/M +retarder/M +retch/DSG +reteach/GS +retention/M +retentive/YP +retentiveness/M +rethink/SGM +rethought +reticence/M +reticent/Y +reticulated +reticulation/MS +reticulum +retina/SM +retinal +retinoblastoma +retinue/SM +retiree/SM +retirement/MS +retort/GMD +retrace/GDS +retract/DBG +retractile +retraction/S +retrain/DGS +retread/D +retrenchment/MS +retribution/MS +retributive +retrieval/SM +retrieve/DRSMZGB +retriever/M +retro/MS +retroactive/Y +retrofire/GDS +retrofit/SM +retrofitted +retrofitting +retrograde/DSG +retrogress/GVDS +retrogression/M +retrorocket/MS +retrospect/MDSGV +retrospection/M +retrospective/MYS +retrovirus/MS +retsina/M +returnable/SM +returnee/SM +rev/ZVM +revamping/M +reveal/GJSD +revealed/U +revealing/Y +reveille/M +revel/JMDRSZG +revelation/SM +revelatory +reveler/M +revelry/SM +revenge/MGDS +revenuer/SM +reverb +reverberate/DSGNX +reverberation/M +revere/DSG +reverence/DSMG +reverend/SM +reverent/Y +reverential/Y +reverie/MS +revers/M +reversal/SM +reverse/Y +reversibility +reversible +reversibly +revert/GSD +revertible +revetment/SM +revile/DRSLZG +revilement/M +reviler/M +reviser/MS +revision/SM +revisionism/M +revisionist/SM +revival/MS +revivalism/M +revivalist/SM +revive/DSG +revivification/M +revocable +revoke/DSG +revolt/GD +revolting/Y +revolution/SM +revolutionary/SM +revolutionist/SM +revolutionize/DSG +revolve/BZGDRS +revolver/M +revue/MS +revulsion/M +revved +revving +rewarded/U +rewarding/U +rewarm/GSD +rewash/GDS +reweave/GS +rewedding +rewind/MB +rewound +rewrite/MGS +rhapsodic +rhapsodical +rhapsodize/GDS +rhapsody/SM +rhea/MS +rhenium/M +rheostat/SM +rhesus/MS +rhetoric/M +rhetorical/Y +rhetorician/SM +rheum/M +rheumatic/MS +rheumatically +rheumatism/M +rheumatoid +rheumy +rhinestone/SM +rhinitis/M +rhino/MS +rhinoceros/MS +rhinoplasty +rhinovirus/MS +rhizome/MS +rho/SM +rhodium/M +rhododendron/SM +rhomboid/SM +rhomboidal +rhombus/MS +rhubarb/MS +rhyme/MZGDRS +rhymer/M +rhymester/MS +rhythm/SM +rhythmic +rhythmical/Y +rial/MS +rib/SM +ribald +ribaldry/M +ribbed +ribber/SM +ribbing +ribbon/SM +riboflavin/M +rice/MZGDRS +ricer/M +rich/TMRSYP +richness/M +rick/GMDS +rickets/M +rickety/RT +rickrack/M +rickshaw/MS +ricochet/GMDS +ricotta/M +rid/S +riddance/M +ridden +ridding +riddle/DSMG +ride/MZGRS +rider/M +riderless +ridership/M +ridge/MGDS +ridgepole/SM +ridgy +ridicule/MGDS +ridiculous/YP +ridiculousness/M +riding/M +rife/TR +riff/GMDS +riffle/DSMG +riffraff/M +rifle/MZGDRS +rifleman/M +riflemen +rifler/M +rifling/M +rift/GMDS +rig/SM +rigatoni/M +rigged +rigger/SM +rigging/M +right/MDRYSPTG +righteous/UP +righteously +righteousness/UM +rightful/PY +rightfulness/M +rightism/M +rightist/SM +rightmost +rightness/M +righto +rightsize/DSG +rightward/S +rigid/YP +rigidity/M +rigidness/M +rigmarole/MS +rigor/MS +rigorous/YP +rigorousness/M +rile/GDS +rill/MS +rim/SGMD +rime/MS +rimless +rimmed +rimming +rind/MS +ring/ZGMDRJ +ringer/M +ringgit/MS +ringleader/MS +ringlet/MS +ringlike +ringmaster/MS +ringside/M +ringtone/SM +ringworm/M +rink/MS +rinse/MGDS +riot/ZGMDRS +rioter/M +rioting/M +riotous/PY +rip/SXTMNR +riparian +ripcord/MS +ripe/YP +ripen/DG +ripened/U +ripeness/M +ripoff/SM +riposte/MGDS +ripped +ripper/SM +ripping +ripple/DSMG +ripply +ripsaw/SM +riptide/MS +rise/JMZGRS +risen +riser/M +risibility/M +risible +rising/M +risk/GMDS +riskily +riskiness/M +risky/RPT +risotto/MS +risque +rissole/S +rite/MS +ritual/SMY +ritualism/M +ritualistic +ritualistically +ritualized +ritzy/RT +riv/ZNR +rival/MDSG +rivaled/U +rivalry/SM +rive/CGDS +river/M +riverbank/SM +riverbed/MS +riverboat/SM +riverfront +riverside/MS +rivet/MDRSZG +riveter/M +riviera/S +rivulet/MS +riyal/MS +rm +roach/GMDS +road/IMS +roadbed/SM +roadblock/MDSG +roadhouse/SM +roadie/MS +roadkill/M +roadrunner/SM +roadshow/SM +roadside/SM +roadster/SM +roadway/SM +roadwork/SM +roadworthy +roam/ZGDRS +roamer/M +roaming/M +roan/MS +roar/ZGMDRS +roarer/M +roaring/M +roast/ZGMDRSJ +roaster/M +roasting/M +rob/S +robbed +robber/MS +robbery/SM +robbing +robe's +robe/EGDS +robin/MS +robocall/SGMD +robot/MS +robotic/S +robotics/M +robotize/GDS +robust/RYPT +robustness/M +rock/ZGMDRS +rockabilly/M +rockbound +rocker/M +rockery/S +rocket/MDSG +rocketry/M +rockfall/SM +rockiness/M +rocky/TRP +rococo/M +rod/SM +rode +rodent/MS +rodeo/MS +roe/SM +roebuck/SM +roentgen/MS +roger/GDS +rogue's +rogue/KS +roguery/M +roguish/YP +roguishness/M +roil/GDS +roister/ZGDRS +roisterer/M +role/MS +roll/MDRZGJS +rollback/SM +roller/M +rollerblading +rollerskating/M +rollick/SDG +rollicking/M +rollmop/S +rollover/SM +romaine/MS +roman/M +romance/MZGDRS +romancer/M +romantic/MS +romantically +romanticism/M +romanticist/SM +romanticize/DSG +romeo/MS +romp/MDRZGS +romper/M +rondo/SM +rood/MS +roof/MDRZGS +roofer/M +roofing/M +roofless +rooftop/SM +rook/MDGS +rookery/SM +rookie/SM +room/MDRZGS +roomer/M +roomette/SM +roomful/SM +roominess/M +roommate/SM +roomy/RTP +roost/SMDRZG +rooster/M +root/MDRZGS +rooter/M +rootkit/SM +rootless/P +rootlet/SM +rope/MZGDRS +roper/M +ropy/RT +rosary/SM +rose/MS +roseate +rosebud/SM +rosebush/MS +rosemary/M +rosette/SM +rosewater/M +rosewood/MS +rosily +rosin/SMDG +rosiness/M +roster/SM +rostrum/MS +rosy/RTP +rot/SM +rota/S +rotary/SM +rotate/DSGNX +rotation/M +rotational +rotatory +rote/M +rotgut/M +rotisserie/SM +rotogravure/MS +rotor/SM +rototiller/MS +rotted +rotten/TPRY +rottenness/M +rotter/S +rotting +rottweiler/S +rotund/P +rotunda/MS +rotundity/M +rotundness/M +roue/MS +rouge/DSMG +rough/MDNRYXTGP +roughage/M +roughcast +roughen/GD +roughhouse/MGDS +roughneck/GMDS +roughness/M +roughs +roughshod +roulette/M +round/PSMDRYZTG +roundabout/SM +roundel/S +roundelay/MS +roundhouse/SM +roundish +roundness/M +roundup/MS +roundworm/SM +rouse/DSG +roust/SDG +roustabout/SM +rout/MRZS +route's +route/ADSG +routeing +router/M +routine/MYS +routinize/GDS +roux +rove/ZGDRS +rover/M +row/SZGMDR +rowan/S +rowboat/MS +rowdily +rowdiness/M +rowdy/PRSMT +rowdyism/M +rowel/SMDG +rower/M +rowing/M +rowlock/S +royal/SMY +royalist/SM +royalties/M +royalty/SM +rpm +rps +rt +rte +rub/SM +rubato/SM +rubbed +rubber/SM +rubberize/GDS +rubberneck/MDRSZG +rubbernecker/M +rubbery +rubbing/S +rubbish/MDSG +rubbishy +rubble/M +rubdown/SM +rube/MS +rubella/M +rubicund +rubidium/M +ruble/SM +rubric/SM +ruby/RSMT +ruched +ruck/DGS +rucksack/MS +ruckus/MS +ructions +rudder/SM +rudderless +ruddiness/M +ruddy/RTP +rude/YTRP +rudeness/M +rudiment/SM +rudimentary +rue/DSMG +rueful/PY +ruefulness/M +ruff/MDYGS +ruffian/MYS +ruffle/DSMG +ruffled/U +rug/SM +rugby/M +rugged/PTRY +ruggedness/M +rugger +rugrat/SM +ruin/MDGS +ruination/M +ruinous/Y +rule/MZGJDRS +ruler/M +ruling/M +rum/SM +rumba/SMDG +rumble/DSJMG +rumbling/M +rumbustious +ruminant/MS +ruminate/XGNVDS +rumination/M +ruminative/Y +rummage/DSMG +rummer +rummest +rummy/M +rumor/SMDG +rumormonger/SM +rump/MYS +rumple/DSMG +rumpus/MS +run/ASM +runabout/MS +runaround/SM +runaway/MS +rundown/SM +rune/MS +rung/MS +runic +runlet/SM +runnel/SM +runner/SM +running/M +runny/RT +runoff/SM +runt/MS +runtime +runty/RT +runway/SM +rupee/SM +rupiah/M +rupiahs +rupture/MGDS +rural +ruse/MS +rush/MDRSZG +rusher/M +rushy +rusk/MS +russet/SM +rust/MDGS +rustic/SM +rustically +rusticate/GDS +rustication/M +rusticity/M +rustiness/M +rustle/DRSJMZG +rustler/M +rustproof/SDG +rusty/RPNT +rut/SM +rutabaga/SM +ruthenium/M +rutherfordium/M +ruthless/YP +ruthlessness/M +rutted +rutting +rutty/RT +rye/M +s/NYXB +sabbath/M +sabbaths +sabbatical/SM +saber/MS +sable/MS +sabot/MS +sabotage/DSMG +saboteur/SM +sabra/MS +sac/SM +saccharin/M +saccharine +sacerdotal +sachem/SM +sachet/SM +sack/ZGMDRJS +sackcloth/M +sacker/M +sackful/MS +sacking/M +sacra +sacrament/MS +sacramental +sacred/YP +sacredness/M +sacrifice/DSMG +sacrificial/Y +sacrilege/MS +sacrilegious/Y +sacristan/MS +sacristy/SM +sacroiliac/MS +sacrosanct/P +sacrosanctness/M +sacrum/M +sad/PY +sadden/SDG +sadder +saddest +saddle's +saddle/UDSG +saddlebag/MS +saddler/S +saddlery +sades +sadhu/S +sadism/M +sadist/SM +sadistic +sadistically +sadness/M +sadomasochism/M +sadomasochist/MS +sadomasochistic +safari/SGMD +safe/MYTPRS +safeguard/SMDG +safekeeping/M +safeness/M +safety/SM +safflower/MS +saffron/MS +sag/SM +saga/MS +sagacious/Y +sagacity/M +sage/MYTRS +sagebrush/M +sagged +sagging +saggy/RT +sago/M +saguaro/MS +sahib/MS +said/U +sail/GMDSJ +sailboard/MRZGS +sailboarder/M +sailboarding/M +sailboat/MS +sailcloth/M +sailfish/MS +sailing/M +sailor/SM +sailplane/MS +saint/MDYS +sainthood/M +saintlike +saintliness/M +saintly/PRT +saith +sake/M +salaam/SMDG +salable/U +salacious/PY +salaciousness/M +salacity/M +salad/MS +salamander/SM +salami/SM +salary/DSM +sale/ABMS +saleroom/S +salesclerk/SM +salesgirl/SM +saleslady/SM +salesman/M +salesmanship/M +salesmen +salespeople/M +salesperson/MS +salesroom/S +saleswoman/M +saleswomen +salience/M +salient/SMY +saline/SM +salinity/M +saliva/M +salivary +salivate/GNDS +salivation/M +sallow/RTP +sallowness/M +sally/DSMG +salmon/SM +salmonella/M +salmonellae +salon/MS +saloon/SM +salsa/MS +salt's +salt/CTGDS +saltbox/MS +saltcellar/SM +salted/U +salter +saltine/SM +saltiness/M +saltpeter/M +saltshaker/SM +saltwater/M +salty/RTP +salubrious/I +salutary +salutation/MS +salutatorian/MS +salutatory +salute/DSMG +salvage/DSMG +salvageable +salvation/M +salve/MZGDRS +salver/M +salvo/MS +samarium/M +samba/MDSG +same/SP +sameness/M +samey +samizdat/S +samosa/S +samovar/SM +sampan/SM +sample/DRSMZGJ +sampler/M +sampling/M +samurai/SM +sanatorium/SM +sanctification/M +sanctify/GDSN +sanctimonious/YP +sanctimoniousness/M +sanctimony/M +sanction/GSMD +sanctioned/U +sanctity/M +sanctuary/SM +sanctum/SM +sand/ZGMDRS +sandal/SM +sandalwood/M +sandbag/SM +sandbagged +sandbagger/SM +sandbagging +sandbank/MS +sandbar/SM +sandblast/ZGMDRS +sandblaster/M +sandbox/MS +sandcastle/MS +sander/M +sandhog/SM +sandiness/M +sandlot/SM +sandlotter/MS +sandman/M +sandmen +sandpaper/GMDS +sandpiper/MS +sandpit/S +sandstone/M +sandstorm/SM +sandwich/MDSG +sandy/RTP +sane/IYTR +saneness/M +sang/S +sangfroid/M +sangria/M +sanguinary +sanguine/Y +sanitarian/SM +sanitarium/SM +sanitary/IU +sanitation/M +sanitize/ZGDRS +sanity/IM +sank +sans +sanserif +sap/SM +sapience/M +sapiens +sapient +sapless +sapling/MS +sapped +sapper/S +sapphire/SM +sappiness/M +sapping +sappy/PRT +saprophyte/SM +saprophytic +sapsucker/SM +sapwood/M +saran/M +sarcasm/MS +sarcastic +sarcastically +sarcoma/MS +sarcophagi +sarcophagus/M +sardine/MS +sardonic +sardonically +sarge/MS +sari/MS +sarky +sarnie/S +sarong/SM +sarsaparilla/MS +sartorial/Y +sash/MS +sashay/SGMD +sass/GMDS +sassafras/MS +sassy/RT +sat +satanic +satanical/Y +satanism/M +satanist/MS +satay +satchel/MS +sate/GDS +sateen/M +satellite/DSMG +satiable/I +satiate/GNDS +satiation/M +satiety/M +satin/M +satinwood/SM +satiny +satire/SM +satiric +satirical/Y +satirist/SM +satirize/DSG +satisfaction/EM +satisfactions +satisfactorily/U +satisfactory/U +satisfied/U +satisfy/EDSG +satisfying/U +satisfyingly +satori/M +satrap/SM +satsuma/S +saturate/DSGN +saturated/U +saturation/M +saturnine +satyr/MS +satyriasis/M +satyric +sauce/MZGDRS +saucepan/SM +saucer/M +saucily +sauciness/M +saucy/RPT +sauerkraut/M +sauna/MDSG +saunter/MDGS +saurian +sauropod/SM +sausage/MS +saute/MS +sauteed +sauteing +savage/DRSMYTGP +savageness/M +savagery/SM +savanna/MS +savant/SM +save/BJMZGDRS +saved/U +saver/M +saving/M +savings/M +savior/SM +savor/MDSG +savoriness/M +savory/PTRSM +savoy/MS +savvy/DRSMTG +saw/SGMD +sawbones/M +sawbuck/MS +sawdust/M +sawfly/SM +sawhorse/SM +sawmill/MS +sawyer/SM +sax/MS +saxifrage/SM +saxophone/MS +saxophonist/SM +say's +say/USG +saying/SM +scab/MS +scabbard/MS +scabbed +scabbiness/M +scabbing +scabby/PTR +scabies/M +scabrous +scad/MS +scaffold/SMG +scaffolding/M +scag/S +scagged +scalability +scalar/S +scalawag/MS +scald/MDSG +scale's +scale/CGDS +scaleless +scalene +scaliness/M +scallion/MS +scallop/GSMD +scalp/MDRSZG +scalpel/SM +scalper/M +scaly/RTP +scam/MS +scammed +scammer/S +scamming +scamp/MRSZ +scamper/GMD +scampi/M +scan/MS +scandal/SM +scandalize/DSG +scandalmonger/SM +scandalous/Y +scandium/M +scanned +scanner/SM +scanning +scansion/M +scant/CDSTG +scanter +scantily +scantiness/M +scantly +scantness/M +scanty/RSPT +scapegoat/SGMD +scapegrace/MS +scapula/M +scapulae +scapular/SM +scar/GMDS +scarab/SM +scarce/RYTP +scarceness/M +scarcity/SM +scare/MS +scarecrow/MS +scaremonger/SMG +scarf/MDSG +scarification/M +scarify/NDSG +scarily +scariness/M +scarlatina/M +scarlet/M +scarp/MDRSZG +scarper/DG +scarred +scarring +scarves +scary/RTP +scat/MS +scathing/Y +scatological +scatology/M +scatted +scatter/GJSMD +scatterbrain/SMD +scattering/M +scattershot +scatting +scatty +scavenge/ZGDRS +scavenger/M +scenario/MS +scenarist/MS +scene/MS +scenery/M +scenic +scenically +scent/CMS +scented/U +scenting +scentless +scepter/MS +sch +schadenfreude +schedule's +schedule/ADSG +scheduled/U +scheduler/S +schema +schemata +schematic/SM +schematically +schematize/GDS +scheme/DRSMZG +schemer/M +scherzo/MS +schilling/MS +schism/SM +schismatic/SM +schist/M +schistosomiasis +schizo/SM +schizoid/MS +schizophrenia/M +schizophrenic/SM +schlemiel/SM +schlep/SM +schlepped +schlepping +schlock/M +schmaltz/M +schmaltzy/TR +schmo/M +schmoes +schmooze/DRSZG +schmuck/MS +schnapps/M +schnauzer/SM +schnitzel/SM +schnook/MS +schnoz/MS +schnozzle/SM +scholar/MYS +scholarship/MS +scholastic +scholastically +scholasticism +school/SGMD +schoolbag/MS +schoolbook/SM +schoolboy/MS +schoolchild/M +schoolchildren/M +schooldays +schooled/U +schoolfellow/SM +schoolgirl/SM +schoolhouse/SM +schooling/M +schoolkid/S +schoolmarm/SM +schoolmarmish +schoolmaster/MS +schoolmate/SM +schoolmistress/MS +schoolroom/SM +schoolteacher/MS +schoolwork/M +schoolyard/SM +schooner/SM +schuss/GMDS +schussboomer/MS +schwa/MS +sci +sciatic +sciatica/M +science/FMS +scientific/U +scientifically/U +scientist/SM +scimitar/SM +scintilla/MS +scintillate/DSGN +scintillation/M +scion/MS +scissor/GDS +scleroses +sclerosis/M +sclerotic +scoff/MDRSZG +scoffer/M +scofflaw/MS +scold/MDSGJ +scolding/M +scoliosis/M +sconce/SM +scone/MS +scoop/MDSG +scoopful/MS +scoot/DRSZG +scooter/M +scope/MGDS +scorbutic +scorch/MDRSZG +scorcher/M +score/MZGDRS +scoreboard/SM +scorecard/MS +scorekeeper/MS +scoreless +scoreline/S +scorer/M +scorn/MDRSZG +scorner/M +scornful/Y +scorpion/MS +scotch/MDSG +scotchs +scoundrel/MS +scour/DRSZG +scourer/M +scourge/DSMG +scout/MDRSZG +scouting/M +scoutmaster/MS +scow/MS +scowl/MDSG +scrabble/MZGDRS +scrabbler/M +scrag/MS +scraggly/RT +scraggy/TR +scram/S +scramble's +scramble/UGDS +scrambler/MS +scrammed +scramming +scrap/MDRSZGJ +scrapbook/SM +scrape/SM +scraper/M +scrapheap/SM +scrapie +scrapped +scrapper/MS +scrapping +scrappy/TR +scrapyard/SM +scratch/GMDS +scratchcard/S +scratched/U +scratchily +scratchiness/M +scratchpad/S +scratchy/PRT +scrawl/SMDG +scrawly +scrawniness/M +scrawny/PTR +scream/SMDRZG +screamer/M +screaming/Y +scree/MDS +screech/GMDS +screechy/TR +screed/S +screen/SJMDG +screening/M +screenplay/SM +screensaver/SM +screenshot/S +screenwriter/SM +screenwriting/M +screw's +screw/UDSG +screwball/MS +screwdriver/MS +screwiness/M +screwworm/SM +screwy/PRT +scribal +scribble/MZGDRS +scribbler/M +scribe's +scribe/CKIS +scrim/MS +scrimmage/MGDS +scrimp/SDG +scrimshaw/MDGS +scrip/MS +script/FSMDG +scripted/U +scriptural +scripture/MS +scriptwriter/SM +scrivener/SM +scrod/M +scrofula/M +scrofulous +scrog/S +scroll/GSMD +scrooge/MS +scrota +scrotal +scrotum/M +scrounge/DRSZG +scrounger/M +scroungy/TR +scrub/MS +scrubbed +scrubber/SM +scrubbing +scrubby/RT +scruff/SM +scruffily +scruffiness/M +scruffy/RPT +scrum/S +scrumhalf +scrumhalves +scrummage/S +scrummed +scrumming +scrump/SGD +scrumptious/Y +scrumpy +scrunch/MDSG +scrunchy/SM +scruple/MGDS +scrupulosity/M +scrupulous/UPY +scrupulousness/UM +scrutineer/S +scrutinize/GDS +scrutiny/M +scuba/MDSG +scud/MS +scudded +scudding +scuff/MDSG +scuffle/MGDS +scull/MDRSZG +sculler/M +scullery/SM +scullion/SM +sculpt/SGD +sculptor/SM +sculptress/MS +sculptural +sculpture/DSMG +scum/MS +scumbag/MS +scummed +scumming +scummy/TR +scupper/MDGS +scurf/M +scurfy +scurrility/M +scurrilous/PY +scurrilousness/M +scurry/GDSM +scurvily +scurvy/TRM +scutcheon/SM +scuttle/MGDS +scuttlebutt/M +scuzzy/TR +scythe/DSMG +sea/SM +seabed/SM +seabird/MS +seaboard/SM +seaborne +seacoast/SM +seafarer/SM +seafaring/M +seafloor/SM +seafood/M +seafront/SM +seagoing +seagull/MS +seahorse/MS +seal's +seal/AUSDG +sealant/MS +sealer/SM +sealskin/M +seam/GMDNS +seaman/M +seamanship/M +seamless/Y +seamount/MS +seamstress/MS +seamy/RT +seance/SM +seaplane/SM +seaport/MS +sear/GMDS +search/AZGMDRS +searchable/U +searcher/AM +searching/Y +searchlight/MS +searing/Y +seascape/SM +seashell/SM +seashore/SM +seasick/P +seasickness/M +seaside/MS +season/SGMDBJ +seasonable/U +seasonably/U +seasonal/Y +seasonality +seasoned/U +seasoning/M +seat's +seat/UGDS +seating/M +seatmate/SM +seawall/MS +seaward/MS +seawater/M +seaway/SM +seaweed/MS +seaworthiness/M +seaworthy/P +sebaceous +seborrhea/M +sebum +sec'y +sec/SM +secant/SM +secateurs +secede/DSG +secession/M +secessionist/MS +seclude/GDS +seclusion/M +seclusive +second/SLZGMDRY +secondarily +secondary/SM +seconder/M +secondhand +secondment/S +secrecy/M +secret/SGVMDY +secretarial +secretariat/MS +secretary/SM +secretaryship/M +secrete/XNS +secretion/M +secretive/PY +secretiveness/M +secretory +sect/IMS +sectarian/MS +sectarianism/M +sectary/SM +section/AESM +sectional/MS +sectionalism/M +sectioned +sectioning +sector/ESM +secular +secularism/M +secularist/SM +secularization/M +secularize/DSG +secure/DRSYTG +secured/U +security/ISM +secy +sedan/MS +sedate/DRSYTGNVP +sedateness/M +sedation/M +sedative/SM +sedentary +sedge/M +sedgy +sediment/MS +sedimentary +sedimentation/M +sedition/M +seditious +seduce/DRSZG +seducer/M +seduction/SM +seductive/YP +seductiveness/M +seductress/MS +sedulous/Y +see/RSMZ +seed's +seed/AGDS +seedbed/MS +seedcase/MS +seeded/U +seeder/SM +seediness/M +seedless +seedling/MS +seedpod/MS +seedy/RPT +seeing/S +seek/ZGRS +seeker/M +seem/GDS +seeming/Y +seemliness/UM +seemly/URTP +seen/U +seep/GDS +seepage/M +seer/M +seersucker/M +seesaw/SMDG +seethe/DSG +segfault/S +segment/GSMD +segmentation/M +segmented/U +segregate/CDSGN +segregated/U +segregation/CM +segregationist/MS +segue/MGDS +segueing +seigneur/SM +seignior/SM +seine/MZGDRS +seiner/M +seismic +seismically +seismograph/ZMR +seismographer/M +seismographic +seismographs +seismography/M +seismologic +seismological +seismologist/MS +seismology/M +seize/GDS +seizure/MS +seldom +select/CSGVD +selection/SM +selective/Y +selectivity/M +selectman/M +selectmen +selectness/M +selector/MS +selenium/M +selenographer/MS +selenography/M +self/M +selfie/SM +selfish/UYP +selfishness/UM +selfless/PY +selflessness/M +selfsame +sell's +sell/AZGRS +seller's +selloff/MS +sellotape/DSG +sellout/MS +seltzer/MS +selvage/MS +selves +semantic/S +semantically +semanticist/MS +semantics/M +semaphore/DSMG +semblance/ASM +semen/M +semester/SM +semi/MS +semiannual/Y +semiarid +semiautomatic/MS +semibreve/S +semicircle/SM +semicircular +semicolon/MS +semiconducting +semiconductor/MS +semiconscious +semidarkness/M +semidetached +semifinal/SM +semifinalist/MS +semigloss/S +semimonthly/SM +seminal +seminar/MS +seminarian/SM +seminary/SM +semiofficial +semiotic/S +semiotics/M +semipermeable +semiprecious +semiprivate +semipro/S +semiprofessional/SM +semiquaver/S +semiretired +semiskilled +semisolid +semisweet +semitone/SM +semitrailer/MS +semitransparent +semitropical +semivowel/SM +semiweekly/SM +semiyearly +semolina/M +sempstress/MS +senate/SM +senator/MS +senatorial +send/ZGRS +sender/M +sendoff/MS +senescence/M +senescent +senile +senility/M +senior/SM +seniority/M +senna/M +senor/MS +senora/SM +senorita/SM +sensation/MS +sensational/Y +sensationalism/M +sensationalist/MS +sensationalize/GDS +sense/MGDS +senseless/PY +senselessness/M +sensibilities +sensibility/IM +sensible/P +sensibleness/M +sensibly/I +sensitive/SMYP +sensitiveness/M +sensitivities +sensitivity/IM +sensitization/CM +sensitize/CDSG +sensor/SM +sensory +sensual/Y +sensualist/SM +sensuality/M +sensuous/YP +sensuousness/M +sent/FAU +sentence/MGDS +sententious/Y +sentience/IM +sentient/I +sentiment/SM +sentimental/Y +sentimentalism/M +sentimentalist/MS +sentimentality/M +sentimentalization/M +sentimentalize/GDS +sentinel/MS +sentry/SM +sepal/MS +separability/IM +separable +separably/I +separate/XMYGNVDSP +separateness/M +separation/M +separatism/M +separatist/MS +separator/MS +sepia/M +sepsis/M +septa +septal +septet/SM +septic +septicemia/M +septicemic +septuagenarian/MS +septum/M +sepulcher/GMDS +sepulchral +seq +sequel/SM +sequence/MZGDRS +sequencing/M +sequential/FY +sequester/SDG +sequestrate/XGNDS +sequestration/M +sequin/SMD +sequinned +sequitur +sequoia/MS +seraglio/MS +serape/SM +seraph/M +seraphic +seraphs +sere/TR +serenade/MGDS +serendipitous +serendipity/M +serene/RPYT +sereneness/M +serenity/M +serf/MS +serfdom/M +serge/M +sergeant/MS +serial/SMY +serialization/SM +serialize/GDSB +series/M +serif/MS +serigraph/M +serigraphs +serine +serious/PY +seriousness/M +sermon/SM +sermonize/GDS +serology/M +serotonin +serous +serpent/MS +serpentine/M +serrate/XND +serration/M +serried +serum/MS +servant/MS +serve's/AF +serve/FACGDS +server/SM +servery/S +service/EMS +serviceability/M +serviceable +serviced +serviceman/M +servicemen +servicewoman/M +servicewomen +servicing +serviette/MS +servile +servility/M +serving's +servings +servitor/MS +servitude/M +servo/MS +servomechanism/SM +servomotor/MS +sesame/SM +sesquicentennial/MS +session/MS +set/AISM +setback/MS +setscrew/SM +setsquare/S +sett/BJZGRS +settee/MS +setter/M +setting/M +settle's +settle/AUGDS +settlement/AM +settlements +settler/SM +setup/MS +seven/MHS +seventeen/SMH +seventeenth/M +seventeenths +seventh/M +sevenths +seventieth/M +seventieths +seventy/SMH +sever/ETGDS +several/MY +severance/SM +severe/YPR +severeness/M +severity/M +sew/ASGD +sewage/M +sewer/MS +sewerage/M +sewing/M +sewn/A +sex/GMDS +sexagenarian/SM +sexily +sexiness/M +sexism/M +sexist/MS +sexless +sexologist/SM +sexology/M +sexpot/MS +sextant/SM +sextet/MS +sexting +sexton/MS +sextuplet/SM +sexual/Y +sexuality/M +sexy/PTR +sf +sh +shabbily +shabbiness/M +shabby/PTR +shack/MDSG +shackle's +shackle/UGDS +shad/GMDSJ +shade/MS +shadily +shadiness/M +shading/M +shadow/SGMD +shadowbox/GDS +shadowy/RT +shady/RPT +shaft/MDSG +shag/MS +shagged +shagginess/M +shagging +shaggy/TPR +shah/M +shahs +shake/MZGRS +shakedown/SM +shaken/U +shakeout/MS +shaker/M +shakeup/MS +shakily +shakiness/M +shaky/RPT +shale/M +shall +shallot/MS +shallow/TPMRYS +shallowness/M +shalom +shalt +sham/GMDS +shaman/SM +shamanic +shamanism +shamanistic +shamble/MGDS +shambles/M +shambolic +shame/MS +shamefaced/Y +shameful/PY +shamefulness/M +shameless/YP +shamelessness/M +shammed +shamming +shampoo/ZGMDRS +shampooer/M +shamrock/MS +shan't +shandy/S +shanghai/DSG +shank/MS +shantung/M +shanty/SM +shantytown/SM +shape's +shape/AGDS +shaped/U +shapeless/YP +shapelessness/M +shapeliness/M +shapely/PTR +shard/MS +share/MZGDRS +shareable +sharecrop/S +sharecropped +sharecropper/MS +sharecropping +shareholder/SM +shareholding/S +sharer/M +shareware/M +sharia/M +shariah +shark/MDSG +sharkskin/M +sharp/MDNRYSPXZTG +sharpen/ADGS +sharpener/MS +sharper/M +sharpie/MS +sharpish +sharpness/M +sharpshooter/SM +sharpshooting/M +shatter/GMDS +shatterproof +shave/MZGDRSJ +shaven/U +shaver/M +shaving/M +shawl/MS +shay/MS +she'd +she'll +she/DSM +sheaf/M +shear/MDRSZG +shearer/M +sheath/JM +sheathe/UGDS +sheathing/M +sheaths +sheave/DSMG +shebang/MS +shebeen/S +shed/MS +shedding +sheen/M +sheeny/TR +sheep/M +sheepdog/MS +sheepfold/SM +sheepherder/MS +sheepish/YP +sheepishness/M +sheepskin/MS +sheer/MDRSPTG +sheerness/M +sheet/MSG +sheeting/M +sheetlike +sheikdom/MS +sheikh/M +sheikhs +sheila/S +shekel/SM +shelf/M +shell/MDRSG +shellac/MS +shellacked +shellacking/MS +shellfire/M +shellfish/MS +shelter/GMDS +shelve/GDS +shelving/M +shenanigan/SM +shepherd/SMDG +shepherdess/MS +sherbet/SM +sheriff/SM +sherry/SM +shew/GDS +shewn +shh +shiatsu/M +shibboleth/M +shibboleths +shield/MDGS +shift/GMDS +shiftily +shiftiness/M +shiftless/PY +shiftlessness/M +shifty/RPT +shiitake/SM +shill/GMDSJ +shillelagh/M +shillelaghs +shilling/M +shim/MS +shimmed +shimmer/SMDG +shimmery +shimming +shimmy/DSMG +shin/ZGMDRS +shinbone/SM +shindig/SM +shine/MS +shiner/M +shingle/DSMG +shinguard/M +shininess/M +shinned +shinning +shinny/DSG +shinsplints/M +shiny/TRP +ship's +ship/ALS +shipboard/MS +shipbuilder/SM +shipbuilding/M +shipload/SM +shipmate/SM +shipment/AM +shipments +shipowner/MS +shipped/A +shipper/SM +shipping/M +shipshape +shipwreck/GMDS +shipwright/MS +shipyard/SM +shire/MS +shirk/ZGDRS +shirker/M +shirr/GMDSJ +shirring/M +shirt/GMDS +shirtfront/SM +shirting/M +shirtless +shirtsleeve/SM +shirttail/SM +shirtwaist/MS +shirty +shit/SM! +shitfaced/! +shithead/S! +shitload/! +shitted/! +shitting/! +shitty/RT! +shiv/ZMRS +shiver/MDG +shivery +shoal/GMDS +shoat/MS +shock/ZGMDRS +shocker/M +shocking/Y +shockproof +shod/U +shoddily +shoddiness/M +shoddy/PRMT +shoe/MS +shoehorn/GMDS +shoeing +shoelace/MS +shoemaker/SM +shoeshine/SM +shoestring/SM +shoetree/MS +shogun/MS +shogunate/M +shone +shoo/GDS +shook +shoot/ZGMRSJ +shooter/M +shooting/M +shootout/MS +shop/MS +shopaholic/MS +shopfitter/S +shopfitting +shopfront/S +shopkeeper/MS +shoplift/DRZGS +shoplifter/M +shoplifting/M +shoppe/MZGDRS +shopper/M +shopping/M +shoptalk/M +shopworn +shore/MGDS +shorebird/SM +shoreline/MS +shoring/M +short/XTGMDNRYSP +shortage/MS +shortbread/M +shortcake/MS +shortchange/DSG +shortcoming/MS +shortcrust +shortcut/MS +shorten/JGD +shortening/M +shortfall/MS +shorthand/MD +shorthorn/MS +shortish +shortlist/DGS +shortness/M +shortsighted/PY +shortsightedness/M +shortstop/MS +shortwave/MS +shorty/SM +shot/MS +shotgun/SM +shotgunned +shotgunning +should +should've +shoulder/MDGS +shouldn't +shout/ZGMDRS +shouter/M +shove/MGDS +shovel/MDSG +shovelful/SM +show/JZGMDRS +showbiz/M +showboat/MDGS +showcase/MGDS +showdown/MS +shower/MDG +showerproof +showery +showgirl/MS +showground/S +showily +showiness/M +showing/M +showjumping +showman/M +showmanship/M +showmen +shown +showoff/SM +showpiece/SM +showplace/SM +showroom/MS +showstopper/MS +showstopping +showtime +showy/TRP +shpt +shrank +shrapnel/M +shred/MS +shredded +shredder/MS +shredding +shrew/MS +shrewd/RYPT +shrewdness/M +shrewish +shriek/MDSG +shrift/M +shrike/MS +shrill/DRSPTG +shrillness/M +shrilly +shrimp/MDRSZG +shrine/MS +shrink/MSBG +shrinkage/M +shrive/GDS +shrivel/SGD +shriven +shroud/GMDS +shrub/MS +shrubbery/SM +shrubby/RT +shrug/MS +shrugged +shrugging +shrunk/N +shtick/MS +shuck/GMDS +shucks/S +shudder/MDSG +shuffle/AMGDS +shuffleboard/SM +shuffler/SM +shun/S +shunned +shunning +shunt/MSDG +shush/DSG +shut/S +shutdown/SM +shuteye/M +shutoff/SM +shutout/SM +shutter/SMDG +shutterbug/MS +shutting +shuttle/DSMG +shuttlecock/GMDS +shy/TGDRSMY +shyer +shyest +shyness/M +shyster/SM +sibilant/SM +sibling/SM +sibyl/MS +sibylline +sic/S +sicced +siccing +sick/PXTGDNRYS +sickbay/S +sickbed/SM +sicken/DG +sickening/Y +sickie/MS +sickish +sickle/MS +sickly/RT +sickness/MS +sicko/MS +sickout/SM +sickroom/MS +side's +side/AGDS +sidearm/SM +sidebar/SM +sideboard/SM +sideburns/M +sidecar/SM +sidekick/SM +sidelight/MS +sideline/DSMG +sidelong +sideman/M +sidemen +sidepiece/MS +sidereal +sidesaddle/MS +sideshow/MS +sidesplitting +sidestep/MS +sidestepped +sidestepping +sidestroke/DSMG +sideswipe/DSMG +sidetrack/SMDG +sidewalk/MS +sidewall/MS +sideways +sidewinder/SM +siding/MS +sidle/MGDS +siege/MS +sienna/M +sierra/MS +siesta/MS +sieve/MGDS +sift/ZGDRS +sifted/U +sifter/M +sigh/GMD +sighs +sight/GMDYSJ +sighting/M +sightless +sightly/UTR +sightread +sightseeing/M +sightseer/MS +sigma/MS +sign's/C +sign/AFCGDS +signage/M +signal/MDRYSZG +signaler/M +signalization/M +signalize/GDS +signalman/M +signalmen +signatory/SM +signature/MS +signboard/MS +signed/U +signer/CMS +signet/MS +significance/IM +significant/IY +signification/M +signify/XDSNG +signing's/C +signings +signor/FMS +signora/SM +signore +signori +signorina/MS +signorine +signpost/GSMD +silage/M +silence/DRSMZG +silencer/M +silent/MRYST +silhouette/DSMG +silica/M +silicate/MS +siliceous +silicon/SM +silicone/M +silicosis/M +silk/MNS +silkily +silkiness/M +silkscreen/SM +silkworm/MS +silky/TRP +sill/MS +silliness/M +silly/TRSMP +silo/MS +silt/GMDS +silty/TR +silver/GMDS +silverfish/MS +silversmith/M +silversmiths +silverware/M +silvery +sim/SM +simian/MS +similar/Y +similarity/ESM +simile/MS +similitude/EM +simmer/GMDS +simonize/DSG +simony/M +simpatico +simper/GMDS +simpering/Y +simple/TRP +simpleminded +simpleness/M +simpleton/SM +simplex +simplicity/M +simplification/M +simplify/DSXNG +simplistic +simplistically +simply +simulacra +simulacrum/S +simulate/EDSGN +simulation/EM +simulations +simulator/EMS +simulcast/GMDS +simultaneity/M +simultaneous/Y +sin/ASM +since +sincere/IYT +sincerer +sincerity/IM +sine/MS +sinecure/MS +sinew/MS +sinewy +sinful/PY +sinfulness/M +sing/BZGMDRYS +singalong/S +singe/MS +singeing +singer/M +singing/M +single/PMGDS +singleness/M +singles/M +singlet/S +singleton/SM +singletree/SM +singsong/SMDG +singular/SMY +singularity/SM +sinister +sink/BZGMRS +sinkable/U +sinker/M +sinkhole/SM +sinless +sinned +sinner/MS +sinning +sinology +sinuosity/M +sinuous/Y +sinus/MS +sinusitis/M +sinusoidal +sip/SM +siphon/GMDS +sipped +sipper/SM +sipping +sir/SXMN +sire/CMGDS +siren/M +sirloin/SM +sirocco/SM +sirrah +sirree/M +sis/MS +sisal/M +sissified +sissy/RSMT +sister/ASM +sisterhood/MS +sisterliness/M +sisterly/P +sit/S +sitar/SM +sitarist/MS +sitcom/SM +site/MGDS +sitemap/SM +sitter/SM +sitting/SM +situ +situate/DSXGN +situation/M +situational +six/MSH +sixfold +sixpence/MS +sixshooter/M +sixteen/SMH +sixteenth/M +sixteenths +sixth/M +sixths +sixtieth/M +sixtieths +sixty/SMH +sizable +size's +size/AGDS +sizer +sizing/M +sizzle/DRSMZG +ska/M +skate/MZGDRS +skateboard/MDRSZG +skateboarder/M +skateboarding/M +skater/M +skating/M +skedaddle/MGDS +skeet/ZMR +skein/MS +skeletal +skeleton/SM +skeptic/SM +skeptical/Y +skepticism/M +sketch/MDRSZG +sketchbook/S +sketcher/M +sketchily +sketchiness/M +sketchpad/S +sketchy/RTP +skew/MDRZGS +skewbald/S +skewer/MDG +ski/SZGMDR +skibob/S +skid/MS +skidded +skidding +skidpan/S +skier/M +skiff/SM +skiffle +skiing/M +skill's +skill/CSD +skilled/U +skillet/SM +skillful/UY +skillfulness/M +skim/MS +skimmed +skimmer/SM +skimming +skimp/SDG +skimpily +skimpiness/M +skimpy/RTP +skin/MS +skincare/M +skinflint/MS +skinful +skinhead/MS +skinless +skinned +skinniness/M +skinning +skinny/RMTP +skint +skintight +skip/MS +skipped +skipper/SMDG +skipping +skirmish/ZGMDRS +skirt/SMDG +skit/MS +skitter/GSD +skittish/YP +skittishness/M +skittle/S +skive/DRSZG +skivvy/DSMG +skoal/SM +skua/S +skulduggery/M +skulk/SDRZG +skulker/M +skull/SM +skullcap/MS +skunk/SMDG +sky/GSM +skycap/SM +skydive/DRSZG +skydiver/M +skydiving/M +skyjack/JZGSDR +skyjacker/M +skyjacking/M +skylark/SGMD +skylight/MS +skyline/SM +skyrocket/GSMD +skyscraper/SM +skyward/S +skywriter/SM +skywriting/M +slab/MS +slabbed +slabbing +slack/PXZTGMDNRYS +slacken/DG +slacker/M +slackness/M +slacks/M +slag/MS +slagged +slagging +slagheap/S +slain +slake/GDS +slalom/MSDG +slam/MS +slammed +slammer/SM +slamming +slander/MZGDRS +slanderer/M +slanderous +slang/M +slangy/RT +slant/MSDG +slanting/Y +slantwise +slap/MS +slapdash +slaphappy +slapped +slapper/S +slapping +slapstick/M +slash/MDRSZG +slasher/M +slat/MDGS +slate/SM +slather/SDG +slatted +slattern/SMY +slaughter/MDRZGS +slaughterer/M +slaughterhouse/MS +slave/DRSMZG +slaveholder/MS +slaver/MDG +slavery/M +slavish/PY +slavishness/M +slaw/M +slay/DRZGJS +slayer/M +slaying/M +sleaze/SM +sleazebag/S +sleazeball/S +sleazily +sleaziness/M +sleazy/PRT +sled/MS +sledded +sledder/SM +sledding +sledge/DSMG +sledgehammer/GSMD +sleek/SDRYTGP +sleekness/M +sleep/SMRZG +sleeper/M +sleepily +sleepiness/M +sleepless/PY +sleeplessness/M +sleepover/SM +sleepwalk/ZGSDR +sleepwalker/M +sleepwalking/M +sleepwear/M +sleepy/RPT +sleepyhead/MS +sleet/SMDG +sleety +sleeve/DSM +sleeveless +sleigh/MDG +sleighs +sleight/SM +slender/PRT +slenderize/DSG +slenderness/M +slept +sleuth/MG +sleuths +slew/MDGS +slice/DRSMZG +slicer/M +slick/SMDRYZTGP +slicker/M +slickness/M +slid +slide/RSMZG +slider/M +slideshow/MS +slight/SMDRYTGP +slightness/M +slim/PS +slime/M +sliminess/M +slimline +slimmed +slimmer/S +slimmest +slimming/M +slimness/M +slimy/RTP +sling/SMG +slingback/S +slingshot/SM +slink/SG +slinky/RT +slip/MS +slipcase/MS +slipcover/MS +slipknot/MS +slippage/MS +slipped +slipper/SM +slipperiness/M +slippery/PRT +slipping +slippy +slipshod +slipstream/SM +slipway/SM +slit/MS +slither/SGMD +slithery +slitter +slitting +sliver/GSMD +slob/MS +slobbed +slobber/MDSG +slobbery +slobbing +sloe/MS +slog/MS +slogan/SM +sloganeering +slogged +slogging +sloop/SM +slop/MDGS +slope/SM +slopped +sloppily +sloppiness/M +slopping +sloppy/PTR +slops/M +slosh/DSG +slot/MS +sloth/M +slothful/YP +slothfulness/M +sloths +slotted +slotting +slouch/ZGMDRS +sloucher/M +slouchy/TR +slough/GMD +sloughs +sloven/SMY +slovenliness/M +slovenly/PTR +slow/DRYTGSP +slowcoach/S +slowdown/SM +slowness/M +slowpoke/SM +sludge/M +sludgy/RT +slue/MGDS +slug/MS +sluggard/MS +slugged +slugger/SM +slugging +sluggish/PY +sluggishness/M +sluice/DSMG +slum/MS +slumber/GSMD +slumberous +slumdog/SM +slumlord/MS +slummed +slummer +slumming +slummy/RT +slump/SMDG +slung +slunk +slur/MS +slurp/SMDG +slurred +slurring +slurry/M +slush/M +slushiness/M +slushy/RPT +slut/MS +sluttish +slutty/RT +sly/TRY +slyness/M +smack/SMDRZG +smacker/M +small/SMRTP +smallholder/S +smallholding/S +smallish +smallness/M +smallpox/M +smarmy/RT +smart/SMDNRYXTGP +smarten/DG +smartness/M +smartphone/SM +smarts/M +smartwatch/MS +smarty/SM +smartypants/M +smash/MDRSZG +smasher/M +smashup/SM +smattering/MS +smear/SMDG +smeary/RT +smell/SMDG +smelliness/M +smelly/RPT +smelt/SMDRZG +smelter/M +smidgen/MS +smilax/M +smile/DSMG +smiley/SM +smiling/Y +smirch/GMDS +smirk/SMDG +smite/SG +smith/M +smithereens/M +smiths +smithy/SM +smitten +smock/SMDG +smocking/M +smog/MS +smoggy/RT +smoke/DRSMZG +smokehouse/MS +smokeless +smoker/M +smokescreen/SM +smokestack/SM +smokey +smokiness/M +smoking/M +smoky/RTP +smolder/SGMD +smooch/MDSG +smoochy +smooth/PDRYTG +smoothie/MS +smoothness/M +smooths +smorgasbord/SM +smote +smother/GSMD +smudge/DSMG +smudgy/TR +smug/YP +smugger +smuggest +smuggle/ZGDRS +smuggler/M +smuggling/M +smugness/M +smurf/S +smut/MS +smuttiness/M +smutty/TRP +snack/SMDG +snaffle/DSMG +snafu/SM +snag/MS +snagged +snagging +snail/SMDG +snake/DSMG +snakebite/MS +snakelike +snakeskin +snaky/RT +snap's +snap/US +snapdragon/SM +snapped/U +snapper/MS +snappily +snappiness/M +snapping/U +snappish/YP +snappishness/M +snappy/TRP +snapshot/SM +snare/DSMG +snarf/SDG +snark/S +snarky/TR +snarl's +snarl/USDG +snarling/Y +snarly/TR +snatch/ZGMDRS +snatcher/M +snazzily +snazzy/TR +sneak/SMDRZG +sneaker/M +sneakily +sneakiness/M +sneaking/Y +sneaky/TRP +sneer/SJMDG +sneering/Y +sneeze/DSMG +snick/SDRZG +snicker/MDG +snide/RYT +sniff/SMDRZG +sniffer/M +sniffle/DSMG +sniffy/RT +snifter/SM +snip/MDRZGS +snipe/SM +sniper/M +snipped +snippet/SM +snipping +snippy/RT +snips/M +snit/MS +snitch/MDSG +snivel/SMDRZG +sniveler/M +snob/MS +snobbery/M +snobbish/PY +snobbishness/M +snobby/RT +snog/S +snogged +snogging +snood/SM +snooker/MDSG +snoop/SMDRZG +snooper/M +snoopy/TR +snoot/SM +snootily +snootiness/M +snooty/PTR +snooze/DSMG +snore/DRSMZG +snorer/M +snorkel/ZGMDRS +snorkeler/M +snorkeling/M +snort/SMDRZG +snorter/M +snot/MS +snottily +snottiness/M +snotty/TPR +snout/SM +snow/MDGS +snowball/GSMD +snowbank/SM +snowbird/SM +snowblower/MS +snowboard/ZGMDRS +snowboarder/M +snowboarding/M +snowbound +snowdrift/SM +snowdrop/SM +snowfall/SM +snowfield/SM +snowflake/SM +snowiness/M +snowline +snowman/M +snowmen +snowmobile/DSMG +snowplow/SGMD +snowshed +snowshoe/SM +snowshoeing +snowstorm/SM +snowsuit/SM +snowy/PRT +snub/MS +snubbed +snubbing +snuff/SMDRYZG +snuffbox/MS +snuffer/M +snuffle/MGDS +snug/MYSP +snugged +snugger +snuggest +snugging +snuggle/MGDS +snugness/M +so +soak/MDGSJ +soaking/M +soap/MDGS +soapbox/MS +soapiness/M +soapstone/M +soapsuds/M +soapy/RPT +soar/MDGS +sob/SM +sobbed +sobbing/Y +sober/SDRYPTG +soberness/M +sobriety/IM +sobriquet/SM +soc +soccer/M +sociability/M +sociable/SM +sociably +social/SMY +socialism/M +socialist/SM +socialistic +socialite/SM +socialization/M +socialize/DSG +societal +society/SM +socioeconomic +socioeconomically +sociological/Y +sociologist/SM +sociology/M +sociopath/M +sociopaths +sociopolitical +sock/MDGS +socket/SM +sockeye/SM +sod/SM +soda/MS +sodded +sodden/Y +sodding +sodium/M +sodomite/MS +sodomize/GDS +sodomy/M +soever +sofa/MS +soft/NRYXTP +softback +softball/MS +softbound +softcover +soften/DRZG +softener/M +softhearted +softness/M +software/M +softwood/SM +softy/SM +soggily +sogginess/M +soggy/RTP +soigne +soignee +soil/MDGS +soiled/U +soiree/SM +sojourn/ZGMDRS +sojourner/M +sol/SM +solace/DSMG +solar +solaria +solarium/M +sold +solder/ZGSMDR +solderer/M +soldier/MDYSG +soldiery/M +sole/FSDGM +solecism/SM +solely +solemn/PTRY +solemness/M +solemnify/DSG +solemnity/SM +solemnization/M +solemnize/DSG +solemnness/M +solenoid/MS +solicit/GDS +solicitation/SM +solicited/U +solicitor/SM +solicitous/PY +solicitousness/M +solicitude/M +solid/PSMRYT +solidarity/M +solidi +solidification/M +solidify/DSNG +solidity/M +solidness/M +solidus/M +soliloquies +soliloquize/DSG +soliloquy/M +solipsism/M +solipsistic +solitaire/MS +solitariness/M +solitary/SMP +solitude/M +solo/MDGS +soloist/MS +solstice/MS +solubility/IM +soluble/MS +solute's +solute/AXN +solutes +solution's/AE +solvable/IU +solve/EADSG +solved/U +solvency/IM +solvent/IMS +solver/SM +somatic +somatosensory +somber/PY +somberness/M +sombrero/MS +some +somebody/SM +someday +somehow +someone/MS +someplace +somersault/MDGS +somerset/SM +somersetted +somersetting +something/SM +sometime/S +someway/S +somewhat/S +somewhere +somnambulism/M +somnambulist/SM +somnolence/M +somnolent +son/SM +sonar/SM +sonata/SM +sonatina/SM +song/MS +songbird/SM +songbook/SM +songfest/SM +songster/MS +songstress/MS +songwriter/SM +songwriting +sonic +sonnet/SM +sonny/SM +sonogram/SM +sonority/M +sonorous/YP +sonorousness/M +sonsofbitches +soon/RT +soot/M +sooth/MDRSZG +soothe +soother/M +soothing/Y +soothsayer/MS +soothsaying/M +sooty/RT +sop/SM +soph +sophism/M +sophist/MS +sophistic +sophistical +sophisticate/DSMGN +sophisticated/U +sophistication/M +sophistry/SM +sophomore/MS +sophomoric +soporific/MS +soporifically +sopped +sopping +soppy/RT +soprano/MS +sorbet/SM +sorcerer/MS +sorceress/MS +sorcery/M +sordid/PY +sordidness/M +sore/MYTRSP +sorehead/MS +soreness/M +sorghum/M +sorority/SM +sorrel/SM +sorrily +sorriness/M +sorrow/SMDG +sorrowful/YP +sorrowfulness/M +sorry/RTP +sort/FASGDM +sorta +sorted/U +sorter/SM +sortie/DSM +sortieing +sot/SM +sottish +sou'wester +sou/SMH +souffle/SM +sough/MDG +soughs +sought/U +souk/S +soul/MS +soulful/YP +soulfulness/M +soulless/YP +soulmate/SM +sound/JPSMDRYZTG +soundalike/S +soundbar/S +soundbite/S +soundboard/MS +soundcheck/S +sounder/M +sounding/M +soundless/Y +soundness/UM +soundproof/GDS +soundproofing/M +soundscape/S +soundtrack/SM +soup/MDGS +soupcon/MS +soupy/RT +sour/MDRYTGSP +source/ADSMG +sourdough/M +sourdoughs +sourish +sourness/M +sourpuss/MS +sousaphone/MS +souse/DSMG +south/M +southbound +southeast/ZMR +southeaster/MY +southeastern +southeastward/S +southerly/SM +southern/SZMR +southerner/M +southernmost +southpaw/SM +southward/MS +southwest/ZMR +southwester/MY +southwestern +southwestward/S +souvenir/SM +sovereign/SM +sovereignty/M +soviet/SM +sow's +sow/ASGD +sower/SM +sown/A +soy/M +soybean/MS +sozzled +spa/SM +space/DRSMZG +spacecraft/MS +spaceflight/MS +spaceman/M +spacemen +spaceport/SM +spacer/M +spaceship/SM +spacesuit/SM +spacetime +spacewalk/SGMD +spacewoman/M +spacewomen +spacey +spacial +spacier +spaciest +spaciness/M +spacing/M +spacious/YP +spaciousness/M +spade/DSMG +spadeful/MS +spadework/M +spadices +spadix/M +spaghetti/M +spake +spam/MS +spammed +spammer/SM +spamming +span/MS +spandex/M +spangle/DSMG +spangly +spaniel/SM +spank/SMDGJ +spanking/M +spanned +spanner/SM +spanning +spar/MS +spare/DRSMYTGP +spareness/M +spareribs/M +sparing/UY +spark/SMDYG +sparkle/DRSMZG +sparkler/M +sparky/RT +sparred +sparring +sparrow/SM +sparrowhawk/S +sparse/RYTP +sparseness/M +sparsity/M +spartan +spasm/SM +spasmodic +spasmodically +spastic/SM +spat/MS +spate/SM +spathe/SM +spatial/Y +spatted +spatter/SGMD +spatting +spatula/SM +spavin/MD +spawn/SMDG +spay/DGS +speak/SRZGJ +speakeasy/SM +speaker/M +speakerphone/S +spear/SMDG +spearfish/GMDS +speargun +spearhead/GMDS +spearmint/M +spec/MS +special/SMY +specialism/S +specialist/MS +specialization/MS +specialize/GDS +specialty/SM +specie/SM +species/M +specif +specifiable +specific/MS +specifically +specification/M +specificity/M +specified/U +specify/XNZDRSG +specimen/SM +specious/YP +speciousness/M +speck/SMDG +speckle/MGDS +specs/M +spectacle/SM +spectacles/M +spectacular/MYS +spectate/DSG +spectator/SM +specter/AMS +spectra +spectral +spectrometer/MS +spectroscope/MS +spectroscopic +spectroscopy/M +spectrum/M +speculate/DSXGNV +speculation/M +speculative/Y +speculator/MS +sped +speech/MS +speechify/DSG +speechless/YP +speechlessness/M +speechwriter/S +speed/SMRZG +speedboat/SM +speeder/M +speedily +speediness/M +speeding/M +speedometer/MS +speedster/SM +speedup/MS +speedway/SM +speedwell/M +speedy/TPR +speleological +speleologist/MS +speleology/M +spell/JSMDRZG +spellbind/ZGRS +spellbinder/M +spellbound +spellcheck/MDRZGS +spellchecker/M +spelldown/SM +speller/M +spelling/M +spelunker/MS +spelunking/M +spend/BSRZG +spender/M +spending/M +spendthrift/MS +spent/U +sperm/SM +spermatozoa +spermatozoon/M +spermicidal +spermicide/MS +spew/MDRZGS +spewer/M +sphagnum/MS +sphere/SM +spherical/Y +spheroid/SM +spheroidal +sphincter/MS +sphinx/MS +spic/S +spice/DSMG +spicily +spiciness/M +spicule/MS +spicy/PRT +spider/SM +spiderweb/MS +spidery +spiel/SMDG +spiff/SDG +spiffy/TR +spigot/SM +spike/DSMG +spikiness/M +spiky/RPT +spill/SMDG +spillage/MS +spillover/SM +spillway/MS +spin/MS +spinach/M +spinal/SMY +spindle/MGDS +spindly/TR +spine/SM +spineless/YP +spinet/SM +spinless +spinnaker/SM +spinner/MS +spinneret/SM +spinney/S +spinning/M +spinster/SM +spinsterhood/M +spinsterish +spiny/RT +spiracle/SM +spiral/SGMDY +spire's +spire/IFAS +spirea/SM +spirit's +spirit/ISGD +spirited/Y +spiritless +spiritual/MYS +spiritualism/M +spiritualist/MS +spiritualistic +spirituality/M +spirituous +spirochete/SM +spiry +spit/MDGS +spitball/SM +spite/ASM +spiteful/PY +spitefuller +spitefullest +spitefulness/M +spitfire/SM +spitted +spitting +spittle/M +spittoon/MS +spiv/S +splanchnic +splash/GMDS +splashdown/MS +splashily +splashiness/M +splashy/RTP +splat/SM +splatted +splatter/GSMD +splatting +splay/SMDG +splayfeet +splayfoot/MD +spleen/SM +splendid/RYT +splendor/MS +splendorous +splenectomy +splenetic +splice/DRSMZG +splicer/M +spliff/S +spline/S +splint/SZGMDR +splinter/MDG +splintery +split/SM +splitting/MS +splodge/S +splosh/DSG +splotch/MDSG +splotchy/TR +splurge/DSMG +splutter/GMDS +spoil's +spoil/CSDRZG +spoilage/M +spoiled/U +spoiler/CM +spoilsport/MS +spoke/SM +spoken/U +spokesman/M +spokesmen +spokespeople +spokesperson/MS +spokeswoman/M +spokeswomen +spoliation/CM +sponge/DRSMZG +sponger/M +sponginess/M +spongy/RPT +sponsor/MDGS +sponsorship/M +spontaneity/M +spontaneous/Y +spoof/SMDG +spook/SMDG +spookiness/M +spooky/RPT +spool/SMDG +spoon/SMDG +spoonbill/MS +spoonerism/MS +spoonful/SM +spoor/SMDG +sporadic +sporadically +spore/DSMG +sporran/S +sport/SMDGV +sportiness/M +sporting/Y +sportive/Y +sportscast/MRZGS +sportscaster/M +sportsman/M +sportsmanlike/U +sportsmanship/M +sportsmen +sportspeople +sportsperson +sportswear/M +sportswoman/M +sportswomen +sportswriter/SM +sporty/TPR +spot/CMS +spotless/PY +spotlessness/M +spotlight/GSMD +spotlit +spotted +spotter/MS +spottily +spottiness/M +spotting +spotty/TPR +spousal/MS +spouse/SM +spout/SMDG +sprain/GSMD +sprang +sprat/SM +sprawl/GSMD +spray's +spray/ASDG +sprayer/MS +spread/ZGBSMR +spreadeagled +spreader/M +spreadsheet/MS +spree/DSM +spreeing +sprig/SM +sprigged +sprightliness/M +sprightly/RTP +spring/GSM +springboard/MS +springbok/MS +springily +springiness/M +springlike +springtime/M +springy/RPT +sprinkle/DRSJMZG +sprinkler/M +sprinkling/M +sprint/ZGSMDR +sprinter/M +sprite/SM +spritz/ZGMDRS +spritzer/M +sprocket/MS +sprog/S +sprout/GSMD +spruce/DRSPMYTG +spruceness/M +sprung +spry/RYT +spryness/M +spud/MS +spume/DSMG +spumoni/M +spumy +spun +spunk/SM +spunky/TR +spur/MS +spurge/M +spurious/PY +spuriousness/M +spurn/SDG +spurred +spurring +spurt/SMDG +sputa +sputnik/MS +sputter/MDGS +sputum/M +spy/GDSM +spyglass/MS +spymaster/S +spyware/M +sq +sqq +squab/SM +squabble/MZGDRS +squabbler/M +squad/SM +squadron/MS +squalid/PTRY +squalidness/M +squall/SGMD +squally +squalor/M +squamous +squander/GDS +square/PDRSMYTG +squareness/M +squarish +squash/GMDS +squashy/TR +squat/SMP +squatness/M +squatted +squatter/MS +squattest +squatting +squaw/SM +squawk/SZGMDR +squawker/M +squeak/SZGMDR +squeaker/M +squeakily +squeakiness/M +squeaky/TRP +squeal/SZGMDR +squealer/M +squeamish/PY +squeamishness/M +squeegee/MDS +squeegeeing +squeeze/BMZGDRS +squeezebox/S +squeezer/M +squelch/GMDS +squelchy +squib/SM +squid/SM +squidgy +squiffy +squiggle/DSMG +squiggly +squint/STGMDR +squire/DSMG +squirm/SGMD +squirmy/RT +squirrel/SGMD +squirt/SGMD +squish/GMDS +squishy/RT +sriracha +ssh +st +stab/MYS +stabbed +stabber/MS +stabbing/MS +stability/IM +stabilization/CM +stabilize/CDSG +stabilizer/MS +stable/DRSMTG +stableman/M +stablemate/S +stablemen +stably/U +staccato/MS +stack/SMDG +stadium/MS +staff's +staff/ASDG +staffer/MS +staffing/M +stag/MDGSJ +stage/SM +stagecoach/MS +stagecraft/M +stagehand/MS +stagestruck +stagflation/M +stagger/MDGS +staggering/Y +staging/M +stagnancy/M +stagnant/Y +stagnate/DSGN +stagnation/M +stagy/RT +staid/PRYT +staidness/M +stain/SMDG +stained/U +stainless/M +stair/SM +staircase/MS +stairway/MS +stairwell/SM +stake/DSMG +stakeholder/MS +stakeout/SM +stalactite/MS +stalagmite/MS +stale/DRSTGP +stalemate/DSMG +staleness/M +stalk/SMDRJZG +stalker/M +stalking/M +stall's +stall/SDG +stallholder/S +stallion/MS +stalwart/MYS +stamen/SM +stamina/M +stammer/ZGMDRS +stammerer/M +stammering/Y +stamp/SMDRZG +stampede/MGDS +stamper/M +stance/ISM +stanch/TGDRS +stanchion/SM +stand/SMRJZG +standalone +standard/MS +standardization/M +standardize/DSG +standby/M +standbys +standee/MS +stander/M +standing/M +standoff/MS +standoffish +standout/MS +standpipe/SM +standpoint/MS +standstill/MS +stank +stanza/SM +staph/M +staphylococcal +staphylococci +staphylococcus/M +staple/DRSMZG +stapler/M +star/MDRZGS +starboard/M +starburst/S +starch/GMDS +starchily +starchiness/M +starchy/PTR +stardom/M +stardust/M +stare/SM +starer/M +starfish/MS +starfruit +stargaze/DRSZG +stargazer/M +stark/RYPZT +starkness/M +starless +starlet/MS +starlight/M +starling/SM +starlit +starred +starring +starry/TR +starstruck +start/ASMDG +starter/MS +startle/GDS +startling/Y +startup/MS +starvation/M +starve/DSJG +starveling/MS +stash/MDSG +stasis +stat/MS +state/DRSMYGNLX +statecraft/M +stated/U +statehood/M +statehouse/MS +stateless/P +statelessness/M +stateliness/M +stately/PRT +statement/AMS +statemented +statementing +stateroom/MS +stateside +statesman/M +statesmanlike +statesmanship/M +statesmen +stateswoman/M +stateswomen +statewide +static/SM +statically +station/MDRZG +stationary +stationer/M +stationery/M +stationmaster/S +statistic/MS +statistical/Y +statistician/SM +statuary/M +statue/SM +statuesque +statuette/MS +stature/MS +status/MS +statute/MS +statutorily +statutory +staunch/PDRSYTG +staunchness/M +stave/DSMG +stay/MDRZGS +std +stdio +stead/SM +steadfast/YP +steadfastness/M +steadily/U +steadiness/UM +steady/TGPDRSM +steak/SM +steakhouse/SM +steal/SMHG +stealth/M +stealthily +stealthiness/M +stealthy/TPR +steam/SMDRZG +steamboat/MS +steamer/M +steamfitter/SM +steamfitting/M +steaminess/M +steampunk +steamroll/ZGDRS +steamroller/MDG +steamship/MS +steamy/TPR +steed/SM +steel/SMDG +steeliness/M +steelmaker/S +steelworker/SM +steelworks/M +steely/PTR +steelyard/SM +steep/SMDNRYPXTG +steepen/GD +steeple/MS +steeplechase/MS +steeplejack/SM +steepness/M +steer/SMDBG +steerage/M +steering/M +steersman/M +steersmen +stegosauri +stegosaurus/MS +stein/SM +stellar +stem/MS +stemless +stemmed +stemming +stemware/M +stench/MS +stencil/GMDS +steno/SM +stenographer/SM +stenographic +stenography/M +stenosis +stent/SM +stentorian +step/IMS +stepbrother/SM +stepchild/M +stepchildren/M +stepdad/MS +stepdaughter/SM +stepfather/SM +stepladder/MS +stepmom/MS +stepmother/SM +stepparent/SM +steppe/DRSMZG +stepper/M +steppingstone/SM +stepsister/MS +stepson/MS +stereo/SM +stereophonic +stereoscope/MS +stereoscopic +stereotype/DSMG +stereotypical +sterile +sterility/M +sterilization/SM +sterilize/DRSZG +sterilizer/M +sterling/M +stern/SMRYPT +sternness/M +sternum/MS +steroid/MS +steroidal +stertorous +stet/S +stethoscope/MS +stetson/MS +stetted +stetting +stevedore/SM +stew/MDGS +steward/GMDS +stewardess/MS +stewardship/M +stick/SMRZG +sticker/M +stickily +stickiness/M +stickleback/SM +stickler/MS +stickpin/MS +stickup/MS +sticky/PTRSM +stiff/SMDNRYPXTG +stiffen/ZGDR +stiffener/M +stiffening/M +stiffness/M +stifle/DSJG +stifling/Y +stigma/SM +stigmata +stigmatic +stigmatization/M +stigmatize/GDS +stile/SM +stiletto/SM +still's +still/ITGSD +stillbirth/M +stillbirths +stillborn +stiller +stillness/M +stilt/SMD +stilted/Y +stimulant/SM +stimulate/DSGNV +stimulation/M +stimuli +stimulus/M +sting/ZGSMR +stinger/M +stingily +stinginess/M +stingray/SM +stingy/RTP +stink/ZGSMR +stinkbug/SM +stinker/M +stinky/RT +stint/GSMD +stipend/SM +stipendiary/S +stipple/DSMG +stippling/M +stipulate/XDSGN +stipulation/M +stir/MS +stirred +stirrer/SM +stirring/SY +stirrup/SM +stitch's +stitch/ADSG +stitchery/M +stitching/M +stoat/SM +stochastic +stock's +stock/AGSD +stockade/DSMG +stockbreeder/MS +stockbroker/SM +stockbroking/M +stockholder/SM +stockily +stockiness/M +stockinette/M +stocking/SM +stockist/S +stockpile/MGDS +stockpot/SM +stockroom/MS +stocktaking/M +stocky/RTP +stockyard/MS +stodge +stodgily +stodginess/M +stodgy/RTP +stogie/MS +stoic/SM +stoical/Y +stoicism/M +stoke/DRSZG +stoker/M +stole/SM +stolen +stolid/RYTP +stolidity/M +stolidness/M +stolon/MS +stomach/MDRZG +stomachache/SM +stomacher/M +stomachs +stomp/GSMD +stone/DRSMZG +stonemason/MS +stoner/M +stonewall/GSD +stoneware/M +stonewashed +stonework/M +stonily +stoniness/M +stonkered +stonking +stony/TRP +stood +stooge/MS +stool/SM +stoop/GSMD +stop's +stop/US +stopcock/SM +stopgap/SM +stoplight/MS +stopover/MS +stoppable/U +stoppage/MS +stopped/U +stopper/GSMD +stopping/U +stopple/DSMG +stopwatch/MS +storage/M +store's +store/ADSG +storefront/MS +storehouse/MS +storekeeper/SM +storeroom/SM +stork/SM +storm/GSMD +stormily +storminess/M +stormy/RPT +story/DSM +storyboard/MS +storybook/SM +storyteller/MS +storytelling/M +stoup/SM +stout/TSMRYP +stouthearted +stoutness/M +stove/SM +stovepipe/SM +stow/DGS +stowage/M +stowaway/MS +straddle/DRSMZG +straddler/M +strafe/MGDS +straggle/DRSZG +straggler/M +straggly/TR +straight/SPXTMNRY +straightaway/SM +straightedge/SM +straighten/ZGDR +straightener/M +straightforward/YPS +straightforwardness/M +straightness/M +straightway +strain's +strain/FADSG +strainer/ASM +strait/MNSX +straiten/GD +straitjacket/SGMD +straitlaced +strand/MDSG +strange/PRYZT +strangeness/M +stranger/M +strangle/ZGDRS +stranglehold/SM +strangler/M +strangulate/GNDS +strangulation/M +strap's +strap/US +strapless/MS +strapped/U +strapping/M +strata +stratagem/SM +strategic/S +strategical/Y +strategics/M +strategist/SM +strategy/SM +strati +stratification/M +stratify/DSGN +stratosphere/SM +stratospheric +stratum/M +stratus/M +straw/GSMD +strawberry/SM +stray/GSMD +streak/MDRSZG +streaker/M +streaky/TR +stream/MDRSZG +streamer/M +streamline/DSG +street/MS +streetcar/MS +streetlamp/S +streetlight/SM +streetwalker/SM +streetwise +strength/M +strengthen/AGDS +strengthener/MS +strengths +strenuous/PY +strenuousness/M +strep/M +streptococcal +streptococci +streptococcus/M +streptomycin/M +stress/MDSG +stressed/U +stressful +stressors +stretch/BZGMDRS +stretcher/MDG +stretchmarks +stretchy/TR +strew/GSDH +strewn +stria/M +striae +striated +striation/MS +stricken +strict/RYPT +strictness/M +stricture/SM +stridden +stride/MGS +stridency/M +strident/Y +strife/M +strike/MZGRSJ +strikebound +strikebreaker/SM +strikebreaking +strikeout/MS +striker/M +striking/Y +string/MDRSZG +stringency/M +stringent/Y +stringer/M +stringiness/M +stringy/PTR +strip/GSMD +stripe/MS +stripey +stripling/MS +stripped +stripper/MS +stripping +striptease/MZGDRS +stripteaser/M +stripy +strive/GS +striven +strobe/MS +stroboscope/MS +stroboscopic +strode +stroke/MGDS +stroll/MDRSZG +stroller/M +strong/RYT +strongbox/MS +stronghold/MS +strongman/M +strongmen +strongroom/S +strontium/M +strop/SM +strophe/SM +strophic +stropped +stroppily +stropping +stroppy/TRP +strove +struck +structural/Y +structuralism +structuralist/S +structure's +structure/AGDS +structured/U +strudel/SM +struggle/MGDS +strum/SM +strummed +strumming +strumpet/MS +strung/UA +strut/SM +strutted +strutting +strychnine/M +stub/MS +stubbed +stubbing +stubble/M +stubbly +stubborn/RYPT +stubbornness/M +stubby/RT +stucco/MDG +stuccoes +stuck/U +stud/MYS +studbook/MS +studded +studding/M +student/SM +studentship/S +studied/U +studiedly +studio/MS +studious/PY +studiousness/M +studly/RT +study's +study/AGDS +stuff/GSMDJ +stuffily +stuffiness/M +stuffing/M +stuffy/RPT +stultification/M +stultify/DSNG +stumble/DRSMZG +stumbler/M +stump/GSMD +stumpy/TR +stun/S +stung +stunk +stunned +stunner/S +stunning/Y +stunt/GSMD +stuntman +stuntmen +stupefaction/M +stupefy/DSG +stupendous/Y +stupid/TMRYS +stupidity/SM +stupor/MS +sturdily +sturdiness/M +sturdy/TRP +sturgeon/SM +stutter/MDRSZG +stutterer/M +sty/SM +style's +style/ADSG +styli +stylish/PY +stylishness/M +stylist/SM +stylistic/S +stylistically +stylize/DSG +stylus/MS +stymie/MDS +stymieing +styptic/SM +suasion/EM +suave/RYTP +suaveness/M +suavity/M +sub/SM +subaltern/MS +subaqua +subarctic +subarea/MS +subatomic +subbasement/SM +subbed +subbing +subbranch/MS +subcategory/SM +subclass +subcommittee/SM +subcompact/SM +subconscious/PMY +subconsciousness/M +subcontinent/SM +subcontinental +subcontract/MDSG +subcontractor/MS +subculture/MS +subcutaneous/Y +subdivide/GDS +subdivision/SM +subdomain/MS +subdominant +subdue/DSG +subeditor/S +subfamily/SM +subfreezing +subgroup/MS +subhead/GJMS +subheading/M +subhuman/MS +subj +subject/GVMDS +subjection/M +subjective/Y +subjectivity/M +subjoin/GDS +subjugate/GNDS +subjugation/M +subjunctive/SM +sublease/MGDS +sublet/SM +subletting +sublieutenant/S +sublimate/GNDS +sublimation/M +sublime/YTGDRS +subliminal/Y +sublimity/M +sublingual +submarginal +submarine/MZRS +submariner/M +submerge/GDS +submergence/M +submerse/GNDS +submersible/MS +submersion/M +submicroscopic +submission/MS +submissive/PY +submissiveness/M +submit/AS +submitted/A +submitter +submitting/A +subnormal +suborbital +suborder/MS +subordinate/DSMGN +subordination/IM +suborn/SGD +subornation/M +subpar +subparagraph +subpart +subplot/MS +subpoena/GMDS +subprime +subprofessional/SM +subprogram/S +subroutine/SM +subscribe/UASDG +subscriber/MS +subscript/MS +subscription/MS +subsection/MS +subsequent/Y +subservience/M +subservient/Y +subset/SM +subside/GDS +subsidence/M +subsidiarity +subsidiary/SM +subsidization/M +subsidize/ZGDRS +subsidizer/M +subsidy/SM +subsist/SDG +subsistence/M +subsoil/M +subsonic +subspace +subspecies/M +substance/SM +substandard +substantial/IY +substantiate/GNDSX +substantiated/U +substantiation/FM +substantive/SMY +substation/MS +substituent +substitute/XMGNDS +substitution/M +substrata +substrate/MS +substratum/M +substructure/SM +subsume/DSG +subsumption +subsurface/M +subsystem/SM +subteen/SM +subtenancy/M +subtenant/SM +subtend/SDG +subterfuge/SM +subterranean +subtext/SM +subtitle/DSMG +subtle/TR +subtlety/SM +subtly +subtopic/SM +subtotal/SGMD +subtract/GSD +subtraction/SM +subtrahend/SM +subtropic/S +subtropical +subtropics/M +suburb/MS +suburban/SM +suburbanite/SM +suburbia/M +subvention/SM +subversion/M +subversive/SPMY +subversiveness/M +subvert/SDG +subway/MS +subzero +succeed/GDS +success/VMS +successful/UY +succession/SM +successive/Y +successor/SM +succinct/RYTP +succinctness/M +succor/SGMD +succotash/M +succubi +succubus +succulence/M +succulency/M +succulent/SM +succumb/GDS +such +suchlike +suck/MDRZGS +sucker/GMD +suckle/DSJG +suckling/M +sucrose/M +suction/SMDG +sudden/PY +suddenness/M +suds/M +sudsy/TR +sue/DSG +suede/M +suet/M +suety +suffer/DRZGSJ +sufferance/M +sufferer/M +suffering/M +suffice/DSG +sufficiency/IM +sufficient/IY +suffix/MDSG +suffixation/M +suffocate/GNDS +suffocation/M +suffragan/MS +suffrage/M +suffragette/SM +suffragist/MS +suffuse/DSGN +suffusion/M +sugar/GSMD +sugarcane/M +sugarcoat/GDS +sugarless +sugarplum/MS +sugary/RT +suggest/GVSDR +suggestibility/M +suggestible +suggestion/SM +suggestive/YP +suggestiveness/M +suicidal +suicide/SM +suit/BMDGS +suitability/UM +suitableness/M +suitably/U +suitcase/SM +suite/SM +suited/U +suiting/M +suitor/MS +sukiyaki/M +sulfa/M +sulfate/SM +sulfide/SM +sulfonamides +sulfur/MDSG +sulfuric +sulfurous +sulk/MDGS +sulkily +sulkiness/M +sulky/TRSMP +sullen/RYPT +sullenness/M +sullied/U +sully/GDS +sultan/MS +sultana/SM +sultanate/MS +sultrily +sultriness/M +sultry/RPT +sum/SM +sumac/M +summarily +summarize/GDS +summary/SM +summat +summation/FMS +summed +summer/MDSG +summerhouse/SM +summertime/M +summery +summing +summit/MS +summitry/M +summon/DRSZG +summoner/M +summons/GMDS +sumo/M +sump/MS +sumptuous/PY +sumptuousness/M +sun/SM +sunbath/ZGMDRS +sunbathe +sunbather/M +sunbathing/M +sunbaths +sunbeam/SM +sunbed/S +sunbelt/SM +sunblock/MS +sunbonnet/SM +sunburn/SGMD +sunburst/MS +sundae/MS +sundeck/S +sunder/DSG +sundial/SM +sundown/SM +sundress/S +sundries/M +sundry/S +sunfish/MS +sunflower/MS +sung/U +sunglasses/M +sunhat/S +sunk/N +sunlamp/SM +sunless +sunlight/M +sunlit +sunned +sunniness/M +sunning +sunny/TRP +sunrise/SM +sunroof/SM +sunscreen/MS +sunset/MS +sunshade/MS +sunshine/M +sunshiny +sunspot/SM +sunstroke/M +suntan/MS +suntanned +suntanning +suntrap/S +sunup/M +sup/SZMR +super/M +superabundance/MS +superabundant +superannuate/GNDS +superannuation/M +superb/RYT +supercargo/M +supercargoes +supercharge/ZGDRS +supercharger/M +supercilious/PY +superciliousness/M +supercity/SM +supercomputer/MS +superconducting +superconductive +superconductivity/M +superconductor/SM +supercritical +superego/MS +supererogation/M +supererogatory +superficial/Y +superficiality/M +superfine +superfluity/M +superfluous/YP +superfluousness/M +superglue +supergrass/S +superhero/MS +superheroes +superhighway/SM +superhuman +superimpose/GDS +superimposition/M +superintend/DSG +superintendence/M +superintendency/M +superintendent/SM +superior/MS +superiority/M +superlative/SMY +superman/M +supermarket/SM +supermassive +supermen +supermodel/SM +supermom/MS +supernal +supernatural/SY +supernova/MS +supernovae +supernumerary/SM +superpose/GDS +superposition/M +superpower/SM +supersaturate/GNDS +supersaturation/M +superscribe/GDS +superscript/MS +superscription/M +supersede/GDS +supersize/GDS +supersonic +superspreader/SM +superstar/MS +superstardom +superstate/S +superstition/MS +superstitious/Y +superstore/MS +superstructure/MS +supertanker/MS +superuser/S +supervene/GDS +supervention/M +supervise/XGNDS +supervised/U +supervision/M +supervisor/MS +supervisory +superwoman/M +superwomen +supine/Y +supp/DRZG +supper/M +suppertime +suppl +supplant/SDG +supple/TLPR +supplement/MDGS +supplemental +supplementary +supplementation/M +suppleness/M +suppliant/SM +supplicant/MS +supplicate/GDS +supplication/M +supplier/M +supply/ZGDRSMXN +support/MDRSBZGV +supportable/UI +supported/U +supporter/M +suppose/GDS +supposed/Y +supposition/MS +suppository/SM +suppress/GVDS +suppressant/MS +suppressible +suppression/M +suppressor/SM +suppurate/DSGN +suppuration/M +supra +supranational +supremacist/MS +supremacy/M +supreme/Y +supremo/S +supt +surcease/DSMG +surcharge/DSMG +surcingle/SM +sure/PYTR +surefire +surefooted +sureness/M +surety/SM +surf/MDRZGS +surface's +surface/AGDS +surfboard/MDSG +surfeit/MDSG +surfer/M +surfing/M +surge/DSMG +surgeon/MS +surgery/SM +surgical/Y +surliness/M +surly/PTR +surmise/MGDS +surmount/DGSB +surmountable/I +surname/MS +surpass/GDS +surpassed/U +surplice/MS +surplus/MS +surplussed +surplussing +surprise/DSMGJ +surprising/UY +surreal +surrealism/M +surrealist/SM +surrealistic +surrealistically +surrender/MDSG +surreptitious/PY +surreptitiousness/M +surrey/MS +surrogacy/M +surrogate/SM +surround/GSDJ +surrounding/M +surroundings/M +surtax/MDSG +surtitle/S +surveillance/M +survey's +survey/ADGS +surveying/M +surveyor/SM +survival/SM +survivalist/SM +survive/DSGB +survivor/SM +susceptibility/SM +susceptible/I +sushi/M +suspect/SMDG +suspected/U +suspend/SDRZG +suspender/M +suspense/XMN +suspenseful +suspension/M +suspicion/SM +suspicious/Y +suss/DSG +sustain/SDBG +sustainability +sustainable/U +sustainably +sustenance/M +sutler/MS +suttee +suture/MGDS +suzerain/MS +suzerainty/M +svelte/TR +swab/MS +swabbed +swabbing +swaddle/DSG +swag/MS +swagged +swagger/SMDRG +swagging +swain/SM +swallow/GSMD +swallowtail/MS +swam +swami/SM +swamp/GSMD +swampland/M +swampy/RT +swan/MS +swank/TGSMDR +swankily +swankiness/M +swanky/RPT +swanned +swanning +swansong/S +swap/MS +swapped +swapping +sward/SM +swarm/GSMD +swarthy/TR +swash/GMDS +swashbuckler/SM +swashbuckling/M +swastika/SM +swat/MS +swatch/MS +swath/GMDS +swathe/M +swaths +swatted +swatter/SMDG +swatting +sway/MDGS +swayback/MD +swayed/U +swear/ZGSR +swearer/M +swearword/MS +sweat/ZGSMDR +sweatband/MS +sweater/M +sweatpants/M +sweats/M +sweatshirt/SM +sweatshop/MS +sweatsuit/S +sweaty/RT +swede/SM +sweep/ZGSMRJ +sweeper/M +sweeping/MY +sweepings/M +sweepstakes/M +sweet/XTSMNRYP +sweetbread/SM +sweetbrier/SM +sweetcorn +sweetened/U +sweetener/MS +sweetening/M +sweetheart/SM +sweetie/SM +sweetish +sweetmeat/MS +sweetness/M +swell/TGSMDRJ +swellhead/MDS +swelling/M +swelter/SGMD +swept +sweptback +swerve/MGDS +swerving/U +swift/PTSMRY +swiftness/M +swig/MS +swigged +swigging +swill/GSMD +swim/MS +swimmer/SM +swimming/MY +swimsuit/SM +swimwear +swindle/DRSMZG +swindler/M +swine/SM +swineherd/SM +swing/ZGSMR +swingeing +swinger/M +swinish +swipe/DSMG +swirl/GSMD +swirly +swish/TGMDRS +switch/MDRSZGB +switchback/MS +switchblade/SM +switchboard/SM +switcher/M +switchover +swivel/MDGS +swiz +swizz +swizzle/DSG +swollen +swoon/SGMD +swoop/SGMD +swoosh/MDSG +sword/SM +swordfish/MS +swordplay/M +swordsman/M +swordsmanship/M +swordsmen +swore +sworn +swot/S +swotted +swotting +swum +swung +sybarite/SM +sybaritic +sycamore/MS +sycophancy/M +sycophant/SM +sycophantic +syllabic +syllabicate/GNDS +syllabication/M +syllabification/M +syllabify/DSNG +syllable/MS +syllabub/S +syllabus/MS +syllogism/MS +syllogistic +sylph/M +sylphic +sylphlike +sylphs +sylvan +symbioses +symbiosis/M +symbiotic +symbiotically +symbol/MS +symbolic +symbolical/Y +symbolism/M +symbolization/M +symbolize/DSG +symbology +symmetric +symmetrical/Y +symmetry/SM +sympathetic/U +sympathetically/U +sympathies/M +sympathize/ZGDRS +sympathizer/M +sympathy/SM +symphonic +symphony/SM +symposium/MS +symptom/MS +symptomatic +symptomatically +syn/H +synagogal +synagogue/SM +synapse/MS +synaptic +sync/MDSG +synchronicity +synchronization/SM +synchronize/GDS +synchronous/Y +synchrony +syncopate/DSGN +syncopation/M +syncope/M +syndicalism +syndicalist/S +syndicate/DSMGN +syndication/M +syndrome/SM +synergism/M +synergistic +synergy/SM +synfuel/MS +synod/SM +synonym/SM +synonymous +synonymy/M +synopses +synopsis/M +synoptic +synovial +syntactic +syntactical/Y +syntax/M +syntheses +synthesis/M +synthesize/ZGDRS +synthesizer/M +synthetic/SM +synthetically +synths +syphilis/M +syphilitic/SM +syringe/DSMG +syrup/SM +syrupy +sysadmin/S +sysop/S +system/SM +systematic/U +systematical/Y +systematization/M +systematize/GDS +systemic/MS +systemically +systole/SM +systolic +t/SDNXGBJ +ta +tab/SM +tabbed +tabbing +tabbouleh/M +tabby/SM +tabernacle/SM +tabla/MS +table/MGDS +tableau/M +tableaux +tablecloth/M +tablecloths +tableland/SM +tablespoon/SM +tablespoonful/SM +tablet/SM +tabletop/MS +tableware/M +tabloid/SM +taboo/MDSG +tabor/MS +tabular +tabulate/DSGNX +tabulation/M +tabulator/SM +tachograph +tachographs +tachometer/SM +tachycardia/M +tachyon +tacit/PY +tacitness/M +taciturn/Y +taciturnity/M +tack/ZGMDRS +tacker/M +tackiness/M +tackle/DRSMZG +tackler/M +tacky/RTP +taco/MS +tact/FM +tactful/YP +tactfulness/M +tactic/SM +tactical/Y +tactician/MS +tactile +tactility/M +tactless/PY +tactlessness/M +tad/SM +tadpole/MS +taffeta/M +taffrail/SM +taffy/SM +tag/SM +tagged +tagger/SM +tagging +tagliatelle +tagline/MS +taiga/MS +tail/ACSDMG +tailback/MS +tailboard/S +tailbone/S +tailcoat/MS +tailgate/MZGDRS +tailgater/M +tailless +taillight/MS +tailor/SGMD +tailoring/M +tailpiece/S +tailpipe/SM +tailspin/SM +tailwind/SM +taint/MDSG +tainted/U +take/AIMS +takeaway/S +taken/A +takeoff/MS +takeout/MS +takeover/SM +taker/MS +taking/SM +takings/M +talc/M +talcum/M +tale/MS +talebearer/MS +talent/SMD +talented/U +tali +talisman/MS +talk/ZGMDRS +talkative/PY +talkativeness/M +talker/M +talkie/RSMT +talky +tall/TRP +tallboy/MS +tallier/M +tallish +tallness/M +tallow/M +tallowy +tally/DRSMZG +tallyho/MDGS +talon/MS +talus/MS +tam/SM +tamale/SM +tamarack/MS +tamarind/MS +tambourine/MS +tame/BYZTGDRSP +tamed/U +tameness/M +tamer/M +tamoxifen +tamp/ZGDRS +tamper/ZGDR +tamperer/M +tampon/SM +tan/SM +tanager/MS +tanbark/M +tandem/SM +tandoori/M +tang/MS +tangelo/MS +tangent/MS +tangential/Y +tangerine/MS +tangibility/IM +tangible/IMS +tangibleness/M +tangibly/I +tangle's +tangle/UDSG +tango/MDSG +tangy/RT +tank/ZGMDRS +tankard/MS +tanker/M +tankful/MS +tanned/U +tanner/SM +tannery/SM +tannest +tannin/M +tanning/M +tansy/M +tantalization/M +tantalize/ZGDRS +tantalizer/M +tantalizing/Y +tantalum/M +tantamount +tantra/M +tantrum/SM +tap/SZGMDR +tapas +tape/MS +tapeline/MS +taper/MDG +tapestry/SM +tapeworm/MS +tapioca/M +tapir/MS +tapped/U +tapper/MS +tappet/MS +tapping +taproom/SM +taproot/SM +tar/SGMD +taramasalata +tarantella/MS +tarantula/SM +tarball/S +tardily +tardiness/M +tardy/TPR +tare/MS +target/MDGS +tariff/MS +tarmac/MS +tarmacadam +tarmacked +tarmacking +tarn/MS +tarnish/GMDS +tarnished/U +taro/MS +tarot/MS +tarp/MS +tarpaulin/MS +tarpon/MS +tarragon/SM +tarred +tarring +tarry/TGDRS +tarsal/MS +tarsi +tarsus/M +tart/PTGMDRYS +tartan/MS +tartar/MS +tartaric +tartness/M +tarty/T +taser/GMDS +task/GMDS +taskbar +taskmaster/MS +taskmistress/MS +tassel/MDSG +taste/JMZGDRS +tasted/U +tasteful/EPY +tastefulness/EM +tasteless/PY +tastelessness/M +taster/M +tastily +tastiness/M +tasting/M +tasty/TRP +tat/SZR +tatami/MS +tater/M +tatted +tatter/MDSG +tatterdemalion/MS +tattie +tatting/M +tattle/MZGDRS +tattler/M +tattletale/MS +tattoo/MDRSZG +tattooer/M +tattooist/SM +tatty/TRS +tau/SM +taught/UA +taunt/ZGMDRS +taunter/M +taunting/Y +taupe/M +taut/PXTNRY +tauten/DG +tautness/M +tautological/Y +tautologous +tautology/SM +tavern/MS +tawdrily +tawdriness/M +tawdry/RTP +tawny/TRM +tax/BZGMDRS +taxa +taxation/M +taxer/M +taxi/GMDS +taxicab/SM +taxidermist/SM +taxidermy/M +taximeter/MS +taxiway/S +taxman +taxmen +taxon +taxonomic +taxonomist/MS +taxonomy/SM +taxpayer/MS +taxpaying +tb/S +tbsp +tea/SM +teabag/S +teacake/SM +teach/ZGRSBJ +teachable/U +teacher/M +teaching/M +teacup/MS +teacupful/MS +teak/MS +teakettle/SM +teal/MS +tealight/MS +team/GMDS +teammate/MS +teamster/MS +teamwork/M +teapot/MS +tear/GMDS +tearaway/S +teardrop/SM +tearful/Y +teargas/MS +teargassed +teargassing +tearjerker/MS +tearoom/SM +teary/TR +tease/MZGDRS +teasel/MS +teaser/M +teasing/Y +teaspoon/SM +teaspoonful/SM +teat/MS +teatime/S +tech/M +techie/S +technetium/M +technical/Y +technicality/SM +technician/SM +technicolor +technique/SM +techno +technobabble +technocracy/SM +technocrat/MS +technocratic +technological/Y +technologist/MS +technology/SM +technophobe/S +techs +tectonic/S +tectonics/M +ted/S +teddy/S +tedious/PY +tediousness/M +tedium/M +tee/DSMH +teeing +teem/GDS +teen/MS +teenage/RZ +teenager/M +teeny/TR +teenybopper/MS +teeter/MDSG +teethe/GDS +teething/M +teetotal/RZ +teetotaler/M +teetotalism/M +tektite/SM +tel +telecast/SZGMR +telecaster/M +telecommunication/MS +telecommunications/M +telecommute/ZGDRS +telecommuter/M +telecommuting/M +teleconference/MGDS +teleconferencing/M +telegenic +telegram/MS +telegraph/MDRZG +telegrapher/M +telegraphese +telegraphic +telegraphically +telegraphist/SM +telegraphs +telegraphy/M +telekinesis/M +telekinetic +telemarketer/SM +telemarketing/M +telemeter/SM +telemetry/SM +teleological +teleology +telepathic +telepathically +telepathy/M +telephone/DRSMZG +telephoner/M +telephonic +telephonist/S +telephony/M +telephoto/SM +telephotography/M +teleplay/MS +teleport +teleportation +teleprinter/MS +teleprocessing/M +teleprompter/SM +telesales +telescope/DSMG +telescopic +telescopically +teletext/MS +telethon/MS +teletype/S +teletypewriter/MS +televangelism/M +televangelist/MS +televise/XGNDS +television/M +teleworker/S +teleworking +telex/MDSG +tell/AGS +teller/SM +telling/Y +telltale/SM +tellurium/M +telly/SM +telnet +temblor/MS +temerity/M +temp/MDRZTGS +temper/MDG +tempera/LSM +temperament/MS +temperamental/Y +temperance/IM +temperate/IY +temperateness/M +temperature/SM +tempest/SM +tempestuous/YP +tempestuousness/M +template's +template/S +temple/SM +tempo/SM +temporal/Y +temporarily +temporariness/M +temporary/FSM +temporize/ZGDRS +temporizer/M +tempt/SDRZG +temptation/MS +tempter/M +tempting/Y +temptress/MS +tempura/M +ten/BMH +tenability/M +tenable/U +tenably +tenacious/YP +tenaciousness/M +tenacity/M +tenancy/SM +tenant/SMDG +tenanted/U +tenantry/M +tench +tend/IEFDGS +tended/U +tendency/SM +tendentious/YP +tendentiousness/M +tender/SMDRYTGP +tenderfoot/MS +tenderhearted/P +tenderheartedness/M +tenderize/ZGDRS +tenderizer/M +tenderloin/SM +tenderness/M +tendinitis/M +tendon/SM +tendril/SM +tenement/SM +tenet/SM +tenfold +tenner/S +tennis/M +tenon/SMDG +tenor/SM +tenpin/SM +tenpins/M +tense/DRSMYTGNXP +tenseness/M +tensile +tension/ESM +tensity/IM +tensor/S +tent/DGSM +tentacle/DSM +tentative/PY +tentativeness/M +tenterhook/MS +tenth/MY +tenths +tenuity/M +tenuous/PY +tenuousness/M +tenure/DSMG +tepee/SM +tepid/YP +tepidity/M +tepidness/M +tequila/SM +terabit/SM +terabyte/MS +terahertz/M +terajoule/S +terapixel/MS +terawatt/S +terbium/M +tercentenary/SM +tercentennial/SM +teriyaki +term/MDYGS +termagant/MS +terminable/IC +terminal/MYS +terminate/DSGNX +termination/CSM +terminator/S +termini +terminological/Y +terminology/SM +terminus/M +termite/SM +tern/IMS +ternary/SM +terr +terrace/DSMG +terracotta/M +terrain/SM +terrapin/MS +terrarium/SM +terrazzo/MS +terrestrial/SMY +terrible/P +terribleness/M +terribly +terrier/M +terrific +terrifically +terrify/GDS +terrifying/Y +terrine/S +territorial/MS +territoriality +territory/SM +terror/SM +terrorism/M +terrorist/SM +terrorize/DSG +terry/RMZ +terrycloth/M +terse/RYTP +terseness/M +tertiary +tessellate/DSXGN +tessellation/M +test's/AFK +test/AKFCDGS +testable/CF +testament/MS +testamentary +testate/S +testator/MS +testatrices +testatrix/M +tested/U +tester/KSM +testes +testicle/MS +testicular +testifier/M +testify/ZGDRS +testily +testimonial/MS +testimony/SM +testiness/M +testings +testis/M +testosterone/M +testy/PRT +tetanus/M +tetchily +tetchy/PRT +tether/SMDG +tetra/SM +tetracycline/M +tetrahedral +tetrahedron/MS +tetrameter/SM +text/FMS +textbook/SM +texted +textile/MS +texting +textual/FY +textural +texture/MGDS +thalami +thalamus/M +thalidomide/M +thallium/M +than +thane/SM +thank/SDG +thankful/YP +thankfulness/M +thankless/PY +thanklessness/M +thanksgiving/SM +that'd +that'll +that/M +thatch/MDRSZG +thatcher/M +thatching/M +thaw/MDGS +the/JG +theater/SM +theatergoer/SM +theatrical/YS +theatricality/M +theatricals/M +theatrics/M +thee/S +theft/SM +their/S +theism/M +theist/SM +theistic +them +thematic +thematically +theme/DSM +themselves +then/M +thence +thenceforth +thenceforward +theocracy/SM +theocratic +theodolite/S +theologian/SM +theological/Y +theology/SM +theorem/MS +theoretic +theoretical/Y +theoretician/SM +theorist/SM +theorize/DSG +theory/SM +theosophic +theosophical +theosophist/SM +theosophy/M +therapeutic/S +therapeutically +therapeutics/M +therapist/SM +therapy/SM +there/M +thereabout/S +thereafter +thereat +thereby +therefor +therefore +therefrom +therein +theremin/SM +thereof +thereon +thereto +theretofore +thereunder +thereunto +thereupon +therewith +therm/SM +thermal/MYS +thermionic +thermodynamic/S +thermodynamics/M +thermometer/MS +thermometric +thermonuclear +thermoplastic/SM +thermos/MS +thermostat/MS +thermostatic +thermostatically +thesauri +thesaurus/MS +these/S +thesis/M +thespian/SM +theta/SM +thew/MS +they +they'd +they'll +they're +they've +thiamine/M +thick/PMNRYXT +thicken/DRJZG +thickener/M +thickening/M +thicket/MS +thickheaded/M +thickness/MS +thicko/S +thickset +thief/M +thieve/DSG +thievery/M +thieving/M +thievish +thigh/M +thighbone/MS +thighs +thimble/MS +thimbleful/SM +thin/YSP +thine +thing/M +thingamabob/SM +thingamajig/SM +thingumabob/S +thingummy/S +thingy/S +think/SRBZG +thinkable/U +thinker/M +thinking's +thinned +thinner/MS +thinness/M +thinnest +thinning +third/SMY +thirst/SGMD +thirstily +thirstiness/M +thirsty/TPR +thirteen/SMH +thirteenth/M +thirteenths +thirtieth/M +thirtieths +thirty/HSM +this +thistle/MS +thistledown/M +thither +tho +thole/SM +thong/SM +thoracic +thorax/MS +thorium/M +thorn/SM +thorniness/M +thorny/PRT +thorough/RYPT +thoroughbred/MS +thoroughfare/MS +thoroughgoing +thoroughness/M +those +thou/MS +though +thought/SM +thoughtful/YP +thoughtfulness/M +thoughtless/PY +thoughtlessness/M +thousand/MHS +thousandfold +thousandth/M +thousandths +thrall/SMDG +thralldom/M +thrash/JMDRSZG +thrasher/M +thrashing/M +thread/SMDRZG +threadbare +threader/M +threadlike +thready/TR +threat/SMNX +threaten/DG +threatening/Y +three/SM +threefold +threepence/M +threescore/MS +threesome/SM +threnody/SM +thresh/MDRSZG +thresher/M +threshold/SM +threw +thrice +thrift/SM +thriftily +thriftiness/M +thriftless +thrifty/PTR +thrill/SMDRZG +thriller/M +thrilling/Y +thrive/DSG +throat/SM +throatily +throatiness/M +throaty/RTP +throb/SM +throbbed +throbbing +throe/SM +thrombi +thrombolytic +thromboses +thrombosis/M +thrombotic +thrombus/M +throne's +throne/S +throng/GSMD +throttle/DRSMZG +throttler/M +through +throughout +throughput/M +throw/SMRZG +throwaway/SM +throwback/SM +thrower/M +thrown +thru +thrum/SM +thrummed +thrumming +thrush/MS +thrust/GSM +thruway/MS +thud/MS +thudded +thudding +thug/MS +thuggery/M +thuggish +thulium/M +thumb/SMDG +thumbnail/SM +thumbprint/SM +thumbscrew/SM +thumbtack/SM +thump/SMDG +thumping/M +thunder/ZGMDRS +thunderbolt/SM +thunderclap/SM +thundercloud/MS +thunderer/M +thunderhead/SM +thunderous/Y +thundershower/SM +thunderstorm/SM +thunderstruck +thundery +thunk/S +thus +thwack/ZGSMDR +thwacker/M +thwart/GSMD +thy +thyme/M +thymine/M +thymus/MS +thyroid/MS +thyroidal +thyself +ti/MRZ +tiara/SM +tibia/M +tibiae +tibial +tic/SM +tick/MDRZGS +ticker/M +ticket/GSMD +ticking/M +tickle/DRSMZG +tickler/M +ticklish/YP +ticklishness/M +ticktacktoe/M +ticktock/MS +tidal/Y +tidbit/SM +tiddler/S +tiddly +tiddlywink/S +tiddlywinks/M +tide/MGJDS +tideland/SM +tidemark/S +tidewater/MS +tideway/MS +tidily/U +tidiness/UM +tidings/M +tidy/DRSMTGP +tie's +tie/AUSD +tieback/MS +tiebreak/RSZ +tiebreaker/M +tiepin/S +tier/MD +tiff/MDGS +tiger/SM +tigerish +tight/SNRYPXT +tighten/ZGDR +tightener/M +tightfisted +tightness/M +tightrope/MS +tights/M +tightwad/MS +tigress/MS +til +tilapia +tilde/SM +tile/MZGDRS +tiler/M +tiling/M +till's +till/EDRZGS +tillable +tillage/M +tiller/EM +tilt/MDGS +timber/SMDG +timberland/M +timberline/MS +timbre/SM +timbrel/SM +time/MYZGJDRS +timekeeper/MS +timekeeping/M +timeless/PY +timelessness/M +timeline/MS +timeliness/UM +timely/UPRT +timeout/SM +timepiece/MS +timer/M +timescale/S +timeserver/SM +timeserving/M +timeshare/S +timestamp/SMD +timetable/DSMG +timeworn +timezone +timid/RYTP +timidity/M +timidness/M +timing/M +timorous/PY +timorousness/M +timothy/M +timpani/M +timpanist/SM +tin/SM +tincture/MGDS +tinder/M +tinderbox/MS +tine/MS +tinfoil/M +ting/MDYG +tinge/SM +tingeing +tingle/DSMGJ +tingling/M +tininess/M +tinker/ZGSMDR +tinkerer/M +tinkle/DSMG +tinned +tinniness/M +tinning +tinnitus/M +tinny/PRT +tinplate/M +tinpot +tinsel/GSMD +tinsmith/M +tinsmiths +tint/MDGS +tintinnabulation/MS +tintype/MS +tinware/M +tiny/RTP +tip/SM +tipped +tipper/SM +tippet/SM +tippex/GDS +tipping +tipple/DRSMZG +tippler/M +tipsily +tipsiness/M +tipster/MS +tipsy/RPT +tiptoe/DSM +tiptoeing +tiptop/SM +tirade/SM +tiramisu/MS +tire's +tire/AGDS +tired/PRYT +tiredness/M +tireless/YP +tirelessness/M +tiresome/PY +tiresomeness/M +tissue/SM +tit/SM +titan/SM +titanic +titanium/M +titch/S +titchy +tithe/DRSMZG +tither/M +titian/M +titillate/DSGN +titillating/Y +titillation/M +titivate/DSGN +titivation/M +title/DSMG +titled/U +titleholder/MS +titlist/MS +titmice +titmouse/M +titter/SGMD +tittle/SM +titty/S +titular +tizz +tizzy/SM +tn +tnpk +to/IU +toad/MS +toadstool/MS +toady/DSMG +toadyism/M +toast/SMDRZG +toaster/M +toastmaster/SM +toastmistress/MS +toasty/TRS +tobacco/MS +tobacconist/SM +toboggan/ZGSMDR +tobogganer/M +tobogganing/M +toccata/S +tocopherol +tocsin/SM +today/M +toddle/DRSMZG +toddler/M +toddy/SM +toe/DSM +toecap/SM +toehold/MS +toeing +toenail/MS +toerag/S +toff/S +toffee/SM +tofu/M +tog/SM +toga/MDS +together/P +togetherness/M +togged +togging +toggle/DSMG +togs/M +toil/MDRZGS +toiler/M +toilet/MDGS +toiletry/SM +toilette/M +toilsome +toke/MGDS +token/SM +tokenism/M +told/AU +tole/M +tolerable/I +tolerably/I +tolerance/IM +tolerances +tolerant/IY +tolerate/GNDS +toleration/M +toll/MDGS +tollbooth/M +tollbooths +tollgate/SM +tollway/SM +toluene/M +tom/SM +tomahawk/SGMD +tomato/M +tomatoes +tomb/MDGS +tombola/S +tomboy/MS +tomboyish +tombstone/MS +tomcat/MS +tome/MS +tomfoolery/SM +tomographic +tomography/M +tomorrow/MS +tomtit/MS +ton/SM +tonal/Y +tonality/SM +tone's +tone/IZGDRS +tonearm/SM +toneless/Y +toner/IM +tong/MDGS +tongue/MGDS +tongueless +tonic/SM +tonight/M +tonnage/SM +tonne/SM +tonsil/MS +tonsillectomy/SM +tonsillitis/M +tonsorial +tonsure/DSMG +tony/RT +too +took/A +tool's +tool/ADGS +toolbar/SM +toolbox/MS +toolkit +toolmaker/MS +toot/MDRZGS +tooter/M +tooth/MD +toothache/MS +toothbrush/MS +toothily +toothless +toothpaste/SM +toothpick/SM +toothsome +toothy/RT +tootle/GDS +tootsie/S +top/SM +topaz/MS +topcoat/SM +topdressing/SM +topee/S +topflight +topi +topiary/M +topic/SM +topical/Y +topicality/M +topknot/SM +topless +topmast/SM +topmost +topnotch +topographer/SM +topographic +topographical/Y +topography/SM +topological/Y +topology +topped +topper/MS +topping/SM +topple/GDS +topsail/SM +topside/SM +topsoil/M +topspin/M +toque/SM +tor/SM +torch/GMDS +torchbearer/MS +torchlight/M +tore +toreador/MS +torment/SMDG +tormenting/Y +tormentor/MS +torn +tornado/M +tornadoes +torpedo/GMD +torpedoes +torpid/Y +torpidity/M +torpor/M +torque/MGDS +torrent/SM +torrential +torrid/YP +torridity/M +torridness/M +torsion/M +torsional +torso/SM +tort's +tort/FEAS +torte/SM +tortellini/M +tortilla/MS +tortoise/MS +tortoiseshell/SM +tortoni/M +tortuous/PY +tortuousness/M +torture/DRSMZG +torturer/M +torturous +torus +tosh +toss/MDRSZG +tossup/MS +tot/SGMD +total/GSMDY +totalitarian/SM +totalitarianism/M +totality/SM +totalizator/SM +tote/MS +totem/SM +totemic +totted +totter/ZGMDRS +totterer/M +totting +toucan/MS +touch/AGMDS +touchdown/SM +touche/BJ +touched/U +touchily +touchiness/M +touching/Y +touchline/S +touchpaper/S +touchscreen/MS +touchstone/MS +touchy/RPT +tough/XTGMDNRYP +toughen/ZGDR +toughener/M +toughie/SM +toughness/M +toughs +toupee/MS +tour/CFSGDM +tourism/M +tourist/MS +touristic +touristy +tourmaline/M +tournament/SM +tourney/MS +tourniquet/MS +tousle/GDS +tout/MDGS +tow/SZGMDR +toward/S +towboat/MS +towel/JGSMD +towelette/SM +toweling/M +tower/GMD +towhead/MDS +towhee/MS +towline/MS +town/MS +townee/S +townhouse/MS +townie/MS +townsfolk/M +township/MS +townsman/M +townsmen +townspeople/M +townswoman/M +townswomen +towpath/M +towpaths +towrope/SM +toxemia/M +toxic +toxicity/SM +toxicological +toxicologist/SM +toxicology/M +toxin/SM +toy/SGMD +toyboy/S +tr +trabecula +trabecular +trabecule +trace/JDRSMZG +traceability +traceable/U +tracer/M +tracery/SM +trachea/M +tracheae +tracheal +tracheotomy/SM +tracing/M +track/ZGSMDR +trackball/SM +tracker/M +trackless +tracksuit/S +tract's +tract/CEKFAS +tractability/IM +tractable/I +tractably/I +traction/EFACKM +tractor/FCKMS +trad +trade/JDRSMZG +trademark/SGMD +trader/M +tradesman/M +tradesmen +tradespeople/M +tradeswoman/M +tradeswomen +trading/M +tradition/MS +traditional/Y +traditionalism/M +traditionalist/SM +traduce/DRSZG +traducer/M +traffic/SM +trafficked +trafficker/SM +trafficking/M +tragedian/SM +tragedienne/MS +tragedy/SM +tragic +tragically +tragicomedy/SM +tragicomic +trail/ZGSMDR +trailblazer/MS +trailblazing/M +trailer/M +train/ZGSMDRB +trained/U +trainee/SM +trainer/M +training/M +trainload/MS +trainman/M +trainmen +trainspotter/S +trainspotting +traipse/DSMG +trait/SM +traitor/SM +traitorous/Y +trajectory/SM +tram/MS +tramcar/S +tramlines +trammed +trammel/SGMD +trammeled/U +tramming +tramp/ZGSMDR +tramper/M +trample/DRSMZG +trampler/M +trampoline/MGDS +tramway/S +trance/MS +tranche/S +tranquil/RYT +tranquility/M +tranquilize/ZGDRS +tranquilizer/M +trans/I +transact/DGS +transaction/SM +transactional +transactor/MS +transatlantic +transceiver/SM +transcend/GSD +transcendence/M +transcendent +transcendental/Y +transcendentalism/M +transcendentalist/SM +transcontinental +transcribe/ZGDRS +transcriber/M +transcript/MS +transcription/SM +transducer/MS +transduction +transect/DSG +transept/MS +transfer/MBS +transferal/MS +transference/M +transferred +transferring +transfiguration/M +transfigure/GDS +transfinite +transfix/DSG +transform/BSZGMDR +transformation/SM +transformational +transformer/M +transfuse/DSXGN +transfusion/M +transgender/S +transgenic +transgress/GDS +transgression/SM +transgressor/SM +transience/M +transiency/M +transient/SMY +transistor/SM +transistorize/DSG +transit/SGMD +transition/GSMD +transitional/Y +transitive/ISMY +transitiveness/M +transitivity/M +transitory +transl +translatable/U +translate/DSGNBX +translated/U +translation/M +translator/SM +transliterate/DSGNX +transliteration/M +translocation +translucence/M +translucency/M +translucent/Y +transmigrate/GNDS +transmigration/M +transmissible +transmission/MS +transmit/S +transmittable +transmittal/M +transmittance/M +transmitted +transmitter/SM +transmitting +transmogrification/M +transmogrify/DSNG +transmutation/SM +transmute/BDSG +transnational/MS +transoceanic +transom/SM +transpacific +transparency/SM +transparent/Y +transphobia/M +transphobic +transpiration/M +transpire/DSG +transplant/MDGS +transplantation/M +transpolar +transponder/SM +transport/BSZGMDR +transportation/M +transporter/M +transpose/DSG +transposition/MS +transsexual/SM +transsexualism/M +transship/SL +transshipment/M +transshipped +transshipping +transubstantiation/M +transversal +transverse/MYS +transvestism/M +transvestite/MS +trap/MS +trapdoor/MS +trapeze/SM +trapezium/SM +trapezoid/SM +trapezoidal +trappable +trapped +trapper/SM +trapping/S +trappings/M +trapshooting/M +trash/GMDS +trashcan/MS +trashiness/M +trashy/RPT +trauma/MS +traumatic +traumatically +traumatize/GDS +travail/SGMD +travel/MDRSZGJ +traveled/U +traveler/M +traveling/M +travelogue/MS +traversal/SM +traverse/DSMG +travesty/GDSM +trawl/ZGSMDR +trawler/M +tray/MS +treacherous/PY +treacherousness/M +treachery/SM +treacle/M +treacly +tread/AGSM +treadle/DSMG +treadmill/MS +treas +treason/BM +treasonous +treasure/DRSMZG +treasurer/M +treasury/SM +treat/AGSMD +treatable +treated/U +treatise/SM +treatment/MS +treaty/SM +treble/MGDS +tree/MDS +treeing +treeless +treelike +treeline +treetop/SM +trefoil/SM +trek/MS +trekked +trekker/SM +trekking +trellis/GMDS +trematode/MS +tremble/DSMG +tremendous/Y +tremolo/SM +tremor/MS +tremulous/PY +tremulousness/M +trench's +trench/ADSG +trenchancy/M +trenchant/Y +trencher/MS +trencherman/M +trenchermen +trend/GSMD +trendily +trendiness/M +trendsetter/S +trendsetting +trendy/RSMPT +trepidation/M +trespass/MDRSZG +trespasser/M +tress/EMS +trestle/MS +trews +trey/MS +triad/SM +triage/MD +trial/ASM +trialed +trialing +triangle/SM +triangular/Y +triangulate/GNDS +triangulation/M +triathlete/S +triathlon/SM +tribal +tribalism/M +tribe/SM +tribesman/M +tribesmen +tribeswoman/M +tribeswomen +tribulation/SM +tribunal/SM +tribune/MS +tributary/SM +tribute's +tribute/FS +trice/M +tricentennial/MS +triceps/MS +triceratops/M +trichina/M +trichinae +trichinosis/M +trick/GSMD +trickery/M +trickily +trickiness/M +trickle/MGDS +trickster/SM +tricky/TRP +tricolor/SM +tricycle/SM +trident/MS +tried/U +triennial/MYS +trier/SM +trifecta/SM +trifle/MZGDRS +trifler/M +trifocals/M +trig/M +trigger/MDSG +triglyceride/MS +trigonometric +trigonometrical +trigonometry/M +trike/SM +trilateral/S +trilby/SM +trill/GSMD +trillion/SMH +trillionth/M +trillionths +trillium/M +trilobite/SM +trilogy/SM +trim/PMYS +trimaran/MS +trimester/SM +trimmed/U +trimmer/SM +trimmest +trimming/SM +trimmings/M +trimness/M +trimonthly +trinitrotoluene/M +trinity/SM +trinket/SM +trio/MS +trip/MYS +tripartite +tripe/M +triple/MGDS +triplet/SM +triplex/MS +triplicate/MGDS +tripod/MS +tripodal +tripos +tripped +tripper/SM +tripping +triptych/M +triptychs +tripwire/S +trireme/SM +trisect/SDG +trisection/M +trite/FPYT +triteness/FM +triter +tritium/M +triumph/GMD +triumphal +triumphalism +triumphalist +triumphant/Y +triumphs +triumvir/MS +triumvirate/SM +trivalent +trivet/MS +trivia/M +trivial/Y +triviality/SM +trivialization/M +trivialize/GDS +trivium/M +trochaic +trochee/SM +trod/AU +trodden/A +troglodyte/SM +troika/MS +troll/SGMD +trolley/SM +trolleybus/MS +trollop/SM +trombone/MS +trombonist/MS +tromp/SGD +tron/S +troop/SZGMDR +trooper/M +troopship/MS +trope/SM +trophy/SM +tropic/MS +tropical/Y +tropics/M +tropism/SM +troposphere/SM +trot/MS +troth/M +trotted +trotter/SM +trotting +troubadour/MS +trouble/DSMG +troubled/U +troublemaker/MS +troubleshoot/DRZGS +troubleshooter/M +troubleshooting/M +troubleshot +troublesome/Y +trough/M +troughs +trounce/DRSZG +trouncer/M +troupe/MZGDRS +trouper/M +trouser/SM +trousers/M +trousseau/M +trousseaux +trout/SM +trove/SM +trow/DSG +trowel/MDSG +troy/S +truancy/M +truant/GMDS +truce/SM +truck/SZGMDR +trucker/M +trucking/M +truckle/MGDS +truckload/SM +truculence/M +truculent/Y +trudge/MGDS +true/MTGDRS +truelove/SM +truffle/MS +trug/S +truism/MS +truly/U +trump/SGMD +trumpery/M +trumpet/ZGMDRS +trumpeter/M +truncate/GNDS +truncation/M +truncheon/SM +trundle/MZGDRS +trundler/M +trunk/SGM +truss/GMDS +trust/ESGMD +trustee/MS +trusteeship/SM +trustful/EY +trustfulness/M +trusting/Y +trustworthiness/M +trustworthy/TPR +trusty/TRSM +truth/ZMR +truther/M +truthful/UYP +truthfulness/UM +truthiness +truths/U +try's +try/AGDS +trying/Y +tryout/SM +tryptophan +tryst/SMDG +tsarists +tsetse/MS +tsp +tsunami/SM +ttys +tub/SZGMDR +tuba/MS +tubal +tubby/TR +tube/MS +tubeless/M +tuber/M +tubercle/SM +tubercular +tuberculin/M +tuberculosis/M +tuberculous +tuberose/M +tuberous +tubful/MS +tubing/M +tubular +tubule/MS +tuck/MDRSZG +tucker/MDG +tuft/MDRSZG +tufter/M +tug/SM +tugboat/MS +tugged +tugging +tuition/IM +tularemia/M +tulip/SM +tulle/M +tum/S +tumble/DRSMZG +tumbledown +tumbler/M +tumbleweed/SM +tumbling/M +tumbrel/SM +tumescence/M +tumescent +tumid +tumidity/M +tummy/SM +tumor/SM +tumorous +tumult/SM +tumultuous/Y +tun/SZGMDR +tuna/MS +tundra/SM +tune/MS +tuneful/YP +tunefulness/M +tuneless/Y +tuner/M +tuneup/SM +tungsten/M +tunic/SM +tunnel/JSMDRZG +tunneler/M +tunny/SM +tuple/S +tuppence +tuppenny +tuque/SM +turban/SMD +turbid +turbidity/M +turbine/SM +turbo/SM +turbocharge/ZGDRS +turbocharger/M +turbofan/SM +turbojet/SM +turboprop/SM +turbot/SM +turbulence/M +turbulent/Y +turd/MS +turducken/SM +tureen/SM +turf/MDSG +turfy +turgid/Y +turgidity/M +turkey/SM +turmeric/SM +turmoil/MS +turn/AMDRSZG +turnabout/SM +turnaround/SM +turnbuckle/SM +turncoat/SM +turner/AM +turning/MS +turnip/SM +turnkey/MS +turnoff/MS +turnout/MS +turnover/MS +turnpike/MS +turnstile/SM +turntable/SM +turpentine/M +turpitude/M +turps +turquoise/SM +turret/SMD +turtle/SM +turtledove/SM +turtleneck/SMD +tush/MS +tusk/MDS +tussle/DSMG +tussock/MS +tussocky +tut/SM +tutelage/M +tutelary +tutor/SMDG +tutored/U +tutorial/SM +tutorship/M +tutted +tutti/SM +tutting +tutu/MS +tux/MS +tuxedo/SM +twaddle/MZGDRS +twaddler/M +twain/M +twang/SMDG +twangy/RT +twas +twat/S +tweak/SMDG +twee +tweed/SM +tweeds/M +tweedy/RT +tween +tweet's +tweet/ASDG +tweeter/SM +tweezers/M +twelfth/M +twelfths +twelve/SM +twelvemonth/M +twelvemonths +twentieth/M +twentieths +twenty/SMH +twerk/SDG +twerp/SM +twice +twiddle/MGDS +twiddly +twig/MS +twigged +twigging +twiggy/TR +twilight/M +twilit +twill/MD +twin/MDRSZG +twine/SM +twiner/M +twinge/DSMG +twink/SY +twinkle/MGJDS +twinkling/M +twinned +twinning +twinset/S +twirl/SMDRZG +twirler/M +twirly +twist's +twist/USDG +twister/MS +twisty/TR +twit/MS +twitch/GMDS +twitchy/RT +twitted +twitter/MDSG +twittery +twitting +twixt +two/SM +twofer/SM +twofold +twopence/SM +twopenny +twosome/SM +twp +tycoon/SM +tying/AU +tyke/MS +tympani/M +tympanic +tympanist/MS +tympanum/SM +type's +type/AGDS +typecast/GS +typeface/MS +typescript/MS +typeset/S +typesetter/MS +typesetting/M +typewrite/RSZG +typewriter/M +typewriting/M +typewritten +typewrote +typhoid/M +typhoon/MS +typhus/M +typical/UY +typicality/M +typification/M +typify/NGDS +typing/M +typist/SM +typo/MS +typographer/SM +typographic +typographical/Y +typography/M +typology/SM +tyrannic +tyrannical/Y +tyrannicidal +tyrannicide/S +tyrannize/GDS +tyrannosaur/MS +tyrannosaurus/MS +tyrannous +tyranny/SM +tyrant/SM +tyro/MS +tzatziki +u/S +ubiquitous/Y +ubiquity/M +udder/SM +ufologist/SM +ufology/M +ugh +ugliness/M +ugly/RTP +uh +uhf +ukase/SM +ukulele/SM +ulcer/SM +ulcerate/XDSGN +ulceration/M +ulcerous +ulna/M +ulnae +ulnar +ulster/MS +ult +ulterior +ultimate/MY +ultimatum/MS +ultimo +ultra/SM +ultraconservative/SM +ultrahigh +ultralight/SM +ultramarine/M +ultramodern +ultrasensitive +ultrashort +ultrasonic +ultrasonically +ultrasound/MS +ultraviolet/M +ululate/DSGNX +ululation/M +um +umbel/SM +umber/M +umbilical +umbilici +umbilicus/M +umbra/SM +umbrage/M +umbrella/SM +umiak/SM +umlaut/MS +ump/SGMD +umpire/MGDS +umpteen/H +unabridged/MS +unacceptability +unacceptable +unaccommodating +unaccountably +unadventurous +unaesthetic +unalterably +unambitious +unanimity/M +unanimous/Y +unapparent +unappetizing +unappreciative +unary +unassertive +unassimilable +unassuming/Y +unavailing/Y +unaware/S +unbeknownst +unbend/SG +unbent +unbid +unblinking/Y +unblushing/Y +unbosom/DG +unbound/D +unbox/GDS +unbreakable +unbroken +uncanny/T +uncap/S +uncaring +uncatalogued +unceasing/Y +unchangeable +uncharacteristic +uncharitable +unchaste/RT +uncial/M +uncle/SM +unclean/DRPT +uncleanly/T +unclear/DRT +uncomfortable +uncommon/T +uncompelling +uncomplaining/Y +uncomplicated +uncomprehending/Y +uncompromising/Y +unconditional/Y +uncongenial +unconscionable +unconscionably +unconscious/M +unconstitutional/Y +uncontrollably +uncontroversial +uncool +uncooperative +uncouth/Y +uncrushable +unction/SM +unctuous/YP +unctuousness/M +uncut +undaunted/Y +undecided/SM +undemonstrative/Y +undeniably +under +underachieve/LZGDRS +underachiever/M +underact/SDG +underage +underappreciated +underarm/SM +underbelly/SM +underbid/S +underbidding +underbrush/M +undercarriage/MS +undercharge/MGDS +underclass/MS +underclassman/M +underclassmen +underclothes/M +underclothing/M +undercoat/GJSMD +undercoating/M +undercover +undercurrent/SM +undercut/SM +undercutting +underdeveloped +underdevelopment/M +underdog/SM +underdone +underemployed +underemployment/M +underestimate/DSMGNX +underestimation/M +underexpose/GDS +underexposure/MS +underfed +underfeed/GS +underfloor +underflow +underfoot +underfunded +underfur/M +undergarment/SM +undergo/G +undergoes +undergone +undergrad/S +undergraduate/SM +underground/MS +undergrowth/M +underhand +underhanded/PY +underhandedness/M +underinflated +underlain +underlay/SM +underlie/S +underline/MGDS +underling/MS +underlip/SM +underlying +undermanned +undermentioned +undermine/GDS +undermost +underneath/M +underneaths +undernourished +undernourishment/M +underpaid +underpants/M +underpart/MS +underpass/MS +underpay/GSL +underpayment/SM +underpin/S +underpinned +underpinning/MS +underplay/DGS +underpopulated +underprivileged +underproduction/M +underrate/GDS +underrepresented +underscore/DSMG +undersea/S +undersecretary/SM +undersell/GS +undersexed +undershirt/SM +undershoot/SG +undershorts/M +undershot +underside/MS +undersign/DGS +undersigned/M +undersized +underskirt/SM +undersold +understaffed +understand/SGBJ +understandably +understanding/MY +understate/DSLG +understatement/SM +understood +understudy/GDSM +undertake/ZGJRS +undertaken +undertaker/M +undertaking/M +underthings/M +undertone/MS +undertook +undertow/SM +underused +underutilized +undervaluation/M +undervalue/DSG +underwater +underway +underwear/M +underweight/M +underwent +underwhelm/DGS +underwire/DS +underworld/MS +underwrite/ZGRS +underwriter/M +underwritten +underwrote +undesirable/MS +undies/M +undo +undoubted/Y +undramatic +undue +undulant +undulate/DSXGN +undulation/M +undying +unearthliness/M +unease/M +uneasy/T +uneatable +uneconomic +unemployed/M +unending +unenterprising +unequal/DY +unerring/Y +unessential +uneven/Y +unexceptionably +unexcited +unexciting +unexpected/YP +unexpectedness/M +unfailing/Y +unfair/PTRY +unfaltering +unfamiliar +unfathomably +unfed +unfeeling/Y +unfeminine +unfit/S +unfitting +unfix/GDS +unflagging/Y +unflappability/M +unflappable +unflappably +unflattering +unflinching/Y +unforgettably +unforgivably +unfortunate/MS +unfriendly/T +unfrock/DG +unfruitful +unfunny +ungainliness/M +ungainly/RPT +ungenerous +ungentle +ungodly/T +ungraceful/Y +ungrudging +unguarded +unguent/SM +ungulate/MS +unhandy/T +unhappy/T +unhealthful +unhealthy/T +unhistorical +unholy/T +unhurt +unicameral +unicellular +unicorn/SM +unicycle/SM +unidirectional +unification/AM +uniform/SMDYG +uniformity/M +unify/AGDSN +unilateral/Y +unilateralism +unimportant +unimpressive +uninformative +uninhibited/Y +uninsured +unintelligent +unintended +uninteresting +uninterrupted/Y +uninterruptible +uninviting +union/ASM +unionism/M +unionist/MS +unique/YTRP +uniqueness/M +unisex/M +unison/M +unitary +unite/AEGSD +unitedly +unities +unitize/DSG +unity/EM +univalent +univalve/SM +universal/MYS +universalism +universalist +universality/M +universalize/DSG +universe/SM +university/SM +univocal +unjust/Y +unkempt +unkind/T +unkindly/T +unknowable/M +unknown/SM +unleaded/M +unless +unlike/PB +unlikely/T +unlit +unlock/DSG +unlovable +unlovely/TR +unloving +unlucky/T +unmanly/T +unmarried +unmeaning +unmentionable/MS +unmentionables/M +unmet +unmindful +unmissable +unmistakably +unmoral +unmovable +unmusical +unnecessary +unnerving/Y +unobservant +unoffensive +unofficial/Y +unoriginal +unpeople +unperceptive +unpersuasive +unpick/GDS +unpin/S +unpleasing +unpolitical +unpopular +unpractical +unprecedented/Y +unprofessional/Y +unpromising +unpropitious +unquestioning/Y +unquiet/TR +unread/B +unready +unreal +unreasoning +unregenerate +unrelated +unrelenting/Y +unrelieved/Y +unremarkable +unremitting/Y +unrepentant +unreported +unrepresentative +unrest/M +unrevealing +unripe/TR +unroll/GDS +unromantic +unruliness/M +unruly/RTP +unsafe/YTR +unsaleable +unsavory +unscathed +unseeing/Y +unseemly/T +unseen/M +unsentimental +unset +unshakable +unshakably +unshapely +unshockable +unshorn +unsightliness/M +unsightly/PT +unsmiling +unsociable +unsocial +unsold +unsound/PRYT +unspeakable +unspeakably +unspecific +unspectacular +unsporting +unstable +unsteady/TRP +unstinting/Y +unstrapping +unsubstantial +unsubtle +unsuitable +unsure +unsuspecting/Y +unsymmetrical +untactful +unthinkably +unthinking/Y +untidy/PTR +until +untimely/T +untiring/Y +untouchable/MS +untoward +untrue/RT +untrustworthy +untruth/M +unutterable +unutterably +unwarrantable +unwary/T +unwavering +unwed +unwelcome/G +unwell +unwieldiness/M +unwieldy/TRP +unwise/RYT +unworried +unworthy/T +unwound +unwrapping +unyielding +up/S +upbeat/MS +upbraid/SGD +upbringing/MS +upchuck/SGD +upcoming +upcountry/M +update/MGDRS +updraft/MS +upend/SGD +upfront +upgrade/MGDS +upheaval/MS +upheld +uphill/MS +uphold/ZGRS +upholder/M +upholster/ASGD +upholsterer/MS +upholstery/M +upkeep/M +upland/MS +uplift/JSMDG +upload/SDG +upmarket +upmost +upon +upped +upper/SM +uppercase/M +upperclassman/M +upperclassmen +upperclasswoman +upperclasswomen +uppercut/MS +uppercutting +uppermost +upping +uppish +uppity +upraise/DSG +uprear/GSD +upright/MYPS +uprightness/M +uprising/SM +upriver +uproar/SM +uproarious/Y +uproot/GSD +upscale +upset/SM +upsetting +upshot/SM +upside/SM +upsilon/MS +upstage/GDS +upstairs +upstanding +upstart/MDSG +upstate/M +upstream +upstroke/SM +upsurge/MGDS +upswing/MS +uptake/SM +uptempo +upthrust/GSM +uptick/SM +uptight +uptown/M +uptrend +upturn/GSMD +upward/SY +upwind +uracil/M +uranium/M +urban +urbane/RYT +urbanity/M +urbanization/M +urbanize/DSG +urbanologist/MS +urbanology/M +urchin/SM +urea/M +uremia/M +uremic +ureter/SM +urethane/M +urethra/M +urethrae +urethral +urge/MGDS +urgency/M +urgent/Y +uric +urinal/SM +urinalyses +urinalysis/M +urinary +urinate/GNDS +urination/M +urine/M +urn/SM +urogenital +urological +urologist/MS +urology/M +ursine +urticaria/M +usability/M +usable/UA +usage/SM +use/AEDSMG +used/U +useful/PY +usefulness/M +useless/YP +uselessness/M +user/MS +username/MS +usher/SMDG +usherette/SM +usu +usual's +usual/UY +usurer/SM +usurious +usurp/SDRZG +usurpation/M +usurper/M +usury/M +utensil/SM +uteri +uterine +uterus/M +utilitarian/MS +utilitarianism/M +utility/SM +utilization/M +utilize/GBDS +utmost/M +utopia/SM +utter/SDYG +utterance/SM +uttermost/M +uveitis +uvula/SM +uvular/MS +uxorious +v/AS +vac/S +vacancy/SM +vacant/Y +vacate/DSG +vacation/ZGMDRS +vacationer/M +vacationist/SM +vaccinate/GNDSX +vaccination/M +vaccine/SM +vacillate/XGNDS +vacillation/M +vacuity/M +vacuole/MS +vacuous/YP +vacuousness/M +vacuum/GSMD +vagabond/SMDG +vagabondage/M +vagarious +vagary/SM +vagina/SM +vaginae +vaginal/Y +vaginitis +vagrancy/M +vagrant/MS +vague/RYTP +vagueness/M +vagus +vain/RYT +vainglorious/Y +vainglory/M +val +valance/MS +vale/MS +valediction/MS +valedictorian/SM +valedictory/SM +valence/MS +valency/SM +valentine/SM +valet/SMDG +valetudinarian/MS +valetudinarianism/M +valiance/M +valiant/Y +valid/Y +validate/IGNDS +validation/IM +validations +validity/IM +validness/M +valise/SM +valley/SM +valor/M +valorous/Y +valuable/MS +valuate/DSG +valuation/CAMS +value's +value/CAGSD +valueless +valuer/SM +valve/DSMG +valveless +valvular +vamoose/DSG +vamp/AMDGS +vampire/SM +van/SM +vanadium/M +vandal/SM +vandalism/M +vandalize/DSG +vane/MS +vanguard/MS +vanilla/SM +vanish/JDSG +vanity/SM +vanned +vanning +vanquish/ZGDRS +vanquisher/M +vantage/SM +vape/GDS +vapid/YP +vapidity/M +vapidness/M +vapor/SM +vaporization/M +vaporize/DRSZG +vaporizer/M +vaporous +vaporware +vapory +vaquero/MS +var/S +variability/IM +variable/ISM +variably/I +variance/SM +variant/MS +variate/NX +variation/M +varicolored +varicose +varied/U +variegate/DSGN +variegation/M +varietal/SM +variety/SM +various/Y +varlet/SM +varmint/MS +varnish/GMDS +varnished/U +varsity/SM +vary/DSG +varying/U +vascular +vase/MS +vasectomy/SM +vasoconstriction +vasomotor +vassal/SM +vassalage/M +vast/MRYTSP +vastness/M +vat/SM +vatted +vatting +vaudeville/M +vaudevillian/MS +vault/SMDRZG +vaulter/M +vaulting/M +vaunt/SMDG +vb +veal/M +vector/SGMD +veejay/SM +veep/MS +veer/MDGS +veg/M +vegan/SM +veganism +vegeburger/S +veges +vegetable/SM +vegetarian/SM +vegetarianism/M +vegetate/GNVDS +vegetation/M +vegged +vegges +veggie/SM +veggieburger/S +vegging +vehemence/M +vehemency/M +vehement/Y +vehicle/MS +vehicular +veil's +veil/UDGS +vein/MDGS +vela +velar/SM +veld/MS +vellum/M +velocipede/MS +velocity/SM +velodrome/S +velour/MS +velum/M +velvet/M +velveteen/M +velvety +venal/Y +venality/M +venation/M +vend/DGS +vendetta/SM +vendible +vendor/MS +veneer/MDGS +venerability/M +venerable +venerate/DSGN +veneration/M +venereal +vengeance/M +vengeful/AY +venial +venireman/M +veniremen +venison/M +venom/M +venomous/Y +venous +vent's +vent/DGS +ventilate/GNDS +ventilation/M +ventilator/SM +ventilatory +ventral +ventricle/SM +ventricular +ventriloquism/M +ventriloquist/SM +ventriloquy/M +venture/DSMG +venturesome/PY +venturesomeness/M +venturous/PY +venturousness/M +venue/ASM +veracious/Y +veracity/M +veranda/SM +verapamil +verb/KMS +verbal/MYS +verbalization/M +verbalize/GDS +verbatim +verbena/SM +verbiage/MS +verbose/Y +verbosity/M +verboten +verdant/Y +verdict/SM +verdigris/GMDS +verdure/M +verge's +verge/FDSG +verger/MS +verifiable/U +verification/M +verified/U +verify/DSNG +verily +verisimilitude/M +veritable +veritably +verity/SM +vermicelli/M +vermiculite/M +vermiform +vermilion/M +vermin/M +verminous +vermouth/M +vernacular/MS +vernal +vernier/SM +veronica/M +verruca/SM +verrucae +versa +versatile +versatility/M +verse/AFNGMSDX +versed/U +versification/M +versifier/M +versify/ZGNDRS +version/AFIMS +versioned +versioning +verso/SM +versus +vert/A +vertebra/M +vertebrae +vertebral +vertebrate/IMS +vertex/MS +vertical/MYS +vertices +vertiginous +vertigo/M +verve/M +very/RT +vesicle/SM +vesicular +vesiculate +vesper/MS +vessel/MS +vest's +vest/ILDGS +vestal/MS +vestibule/MS +vestige/SM +vestigial/Y +vesting/M +vestment/IMS +vestry/SM +vestryman/M +vestrymen +vet/SM +vetch/MS +veteran/SM +veterinarian/MS +veterinary/SM +veto/MDG +vetoes +vetted +vetting +vex/GDS +vexation/SM +vexatious/Y +vhf +vi +via +viability/M +viable +viably +viaduct/SM +vial/MS +viand/SM +vibe/MS +vibes/M +vibraharp/SM +vibrancy/M +vibrant/Y +vibraphone/MS +vibraphonist/MS +vibrate/GNDSX +vibration/M +vibrato/MS +vibrator/SM +vibratory +viburnum/SM +vicar/SM +vicarage/SM +vicarious/YP +vicariousness/M +vice/CMS +viced +vicegerent/SM +vicennial +viceregal +viceroy/MS +vichyssoise/M +vicing +vicinity/M +vicious/YP +viciousness/M +vicissitude/SM +victim/MS +victimization/M +victimize/GDS +victimless +victor/MS +victorious/Y +victory/SM +victual/SMDG +vicuna/MS +videlicet +video/GSMD +videocassette/SM +videoconferencing +videodisc/MS +videophone/MS +videotape/DSMG +videotex +vie/DS +view/AMDRSZG +viewable +viewer/AM +viewership/M +viewfinder/SM +viewing/SM +viewpoint/MS +vigesimal +vigil/SM +vigilance/M +vigilant/Y +vigilante/SM +vigilantism/M +vigilantist/M +vignette/DSMG +vignettist/MS +vigor/M +vigorous/Y +vii +viii +viking/MS +vile/YTPR +vileness/M +vilification/M +vilify/DSNG +villa/SM +village/RSMZ +villager/M +villain/SM +villainous +villainy/SM +villein/SM +villeinage/M +villi +villus/M +vim/M +vinaigrette/M +vincible/I +vindicate/XDSGN +vindication/M +vindicator/MS +vindictive/PY +vindictiveness/M +vine/MS +vinegar/M +vinegary +vineyard/MS +vino/M +vinous +vintage/MS +vintner/MS +vinyl/SM +viol/MBS +viola/SM +violable/I +violate/GNDSX +violation/M +violator/SM +violence/M +violent/Y +violet/MS +violin/MS +violincello/S +violinist/SM +violist/MS +violoncellist/SM +violoncello/MS +viper/SM +viperous +virago/M +viragoes +viral +vireo/SM +virgin/MS +virginal/SM +virginity/M +virgule/MS +virile +virility/M +virologist/SM +virology/M +virtual/Y +virtualization +virtue/SM +virtuosity/M +virtuoso/M +virtuous/YP +virtuousness/M +virulence/M +virulent/Y +virus/MS +visa/MDSG +visage/MS +viscera +visceral/Y +viscid +viscose/M +viscosity/M +viscount/SM +viscountcy/SM +viscountess/MS +viscous +viscus/M +vise/ACMGDS +visibility/IM +visible/I +visibly/I +vision/KGDSM +visionary/SM +visit's +visit/ASGD +visitant/MS +visitation/MS +visitor/MS +visor/SM +vista/SM +visual/SMY +visualization/SM +visualize/DRSZG +visualizer/M +vita/M +vitae +vital/SY +vitality/M +vitalization/AM +vitalize/CAGSD +vitals/M +vitamin/MS +vitiate/GNDS +vitiation/M +viticulture/M +viticulturist/MS +vitreous +vitrifaction/M +vitrification/M +vitrify/GNDS +vitrine/SM +vitriol/M +vitriolic +vitriolically +vittles/M +vituperate/GNVDS +vituperation/M +viva/MS +vivace +vivacious/PY +vivaciousness/M +vivacity/M +vivaria +vivarium/SM +vivid/RYTP +vividness/M +vivify/ADSG +viviparous +vivisect/DGS +vivisection/M +vivisectional +vivisectionist/SM +vixen/SM +vixenish/Y +viz +vizier/SM +vlf +vocab +vocable/MS +vocabulary/SM +vocal/SMY +vocalic +vocalist/SM +vocalization/MS +vocalize/DSG +vocation/FIKASM +vocational/Y +vocative/MS +vociferate/DSGN +vociferation/M +vociferous/YP +vociferousness/M +vodka/SM +vogue/SM +voguish +voice/IDSMG +voiced/U +voiceless/PY +voicelessness/M +voicemail/SM +void/MDSGB +voila +voile/M +vol/S +volatile +volatility/M +volatilize/DSG +volcanic +volcanism +volcano/M +volcanoes +vole/MS +volition/M +volitional +volley/GSMD +volleyball/MS +volt/AMS +voltage/MS +voltaic +voltmeter/SM +volubility/M +voluble +volubly +volume/SM +volumetric +voluminous/YP +voluminousness/M +voluntarily/I +voluntarism/M +voluntary/SM +volunteer/SGMD +volunteerism/M +voluptuary/SM +voluptuous/PY +voluptuousness/M +volute/SM +vomit/SMDG +voodoo/GSMD +voodooism/M +voracious/PY +voraciousness/M +voracity/M +vortex/MS +votary/SM +vote's +vote/CGVDS +voter/SM +vouch/DRSZG +voucher/M +vouchsafe/DSG +vow/SGMD +vowel/SM +voyage/MZGDRS +voyager/M +voyageur/SM +voyeur/MS +voyeurism/M +voyeuristic +vulcanization/M +vulcanize/GDS +vulgar/RYT +vulgarian/MS +vulgarism/MS +vulgarity/SM +vulgarization/M +vulgarize/ZGDRS +vulgarizer/M +vulnerabilities +vulnerability/IM +vulnerable/I +vulnerably/I +vulpine +vulture/SM +vulturous +vulva/M +vulvae +vuvuzela/MS +vying +w/DNXTGVJ +wabbit/S +wack/MRTS +wackiness/M +wacko/SM +wacky/RPT +wad/SZGMDR +wadded +wadding/M +waddle/DSMG +wade/MS +wader/M +waders/M +wadge/S +wadi/MS +wafer/SM +waffle/MZGDRS +waffler/M +waft/MDGS +wag/SZGMDR +wage/MS +waged/U +wager/ZGMDR +wagerer/M +wagged +waggery/SM +wagging +waggish/YP +waggishness/M +waggle/MGDS +wagon/ZSMR +wagoner/M +wagtail/SM +waif/MS +wail/MDRZGS +wailer/M +wailing/M +wain/MS +wainscot/SJMDG +wainscoting/M +wainwright/MS +waist/SM +waistband/MS +waistcoat/MS +waistline/MS +wait/MDRZGS +waiter/M +waiting/M +waitperson/MS +waitress/MS +waitstaff/M +waive/DRSZG +waiver/M +wake/MGJDS +wakeful/PY +wakefulness/M +waken/GSD +waldo/S +waldoes +wale/MGDS +walk/MDRZGS +walkabout/S +walkaway/MS +walker/M +walkies +walking/M +walkout/SM +walkover/MS +walkway/SM +wall/MDGS +wallaby/SM +wallah +wallahs +wallboard/M +wallet/MS +walleye/DSM +wallflower/MS +wallop/MDSJG +walloping/M +wallow/MDSG +wallpaper/SMDG +wally/S +walnut/MS +walrus/MS +waltz/ZGMDRS +waltzer/M +wampum/M +wan/GPDY +wand/MS +wander/DRSJZG +wanderer/M +wanderings/M +wanderlust/SM +wane/MS +wangle/MZGDRS +wangler/M +wank/DRZGS +wanna +wannabe/SM +wannabee/S +wanner +wanness/M +wannest +want/MDGS +wanted/U +wanton/MDYSPG +wantonness/M +wapiti/MS +war/SM +warble/MZGDRS +warbler/M +warbonnet/SM +ward/AMDGS +warden/MS +warder/MS +wardress/S +wardrobe/SM +wardroom/SM +ware/MS +warehouse/DSMG +warez +warfare/M +warfarin +warhead/MS +warhorse/SM +warily/U +wariness/UM +warlike +warlock/MS +warlord/MS +warm/PDRYHZTGS +warmblooded +warmer/M +warmhearted/P +warmheartedness/M +warmish +warmness/M +warmonger/SMG +warmongering/M +warmth/M +warn/JDGS +warning/M +warp/MDGS +warpaint +warpath/M +warpaths +warplane/MS +warrant/GMDS +warranted/U +warranty/DSMG +warred +warren/MS +warring +warrior/SM +warship/SM +wart/MS +warthog/SM +wartime/M +warty/TR +wary/UPRT +was +wasabi +wash/BJMDRSZG +washable/SM +washbasin/SM +washboard/SM +washbowl/SM +washcloth/M +washcloths +washed/U +washer/M +washerwoman/M +washerwomen +washing/M +washout/MS +washrag/MS +washroom/MS +washstand/SM +washtub/MS +washy/TR +wasn't +wasp/MS +waspish/YP +waspishness/M +wassail/SMDG +wast +wastage/M +waste/DRSMZG +wastebasket/MS +wasteful/PY +wastefulness/M +wasteland/SM +wastepaper/M +waster/M +wastewater +wastrel/SM +watch/BZGMDRS +watchable/U +watchband/MS +watchdog/SM +watcher/M +watchful/YP +watchfulness/M +watchmaker/MS +watchmaking/M +watchman/M +watchmen +watchstrap/S +watchtower/SM +watchword/MS +water/GSMD +waterbed/MS +waterbird/SM +waterboard/MDJSG +waterboarding/M +waterborne +watercolor/MS +watercourse/SM +watercraft/M +watercress/M +waterfall/SM +waterfowl/SM +waterfront/MS +waterhole/SM +wateriness/M +waterlily/SM +waterline/MS +waterlogged +watermark/MDGS +watermelon/SM +watermill/MS +waterproof/SMDG +waterproofing/M +waters/M +watershed/MS +waterside/MS +waterspout/SM +watertight +waterway/MS +waterwheel/SM +waterworks/M +watery/PTR +watt/MS +wattage/M +wattle/MGDS +wave/MZGDRS +waveband/S +waveform +wavefront +wavelength/M +wavelengths +wavelet/SM +wavelike +waver/ZGMDR +waverer/M +wavering/Y +waviness/M +wavy/PRT +wax/GMDNS +waxiness/M +waxwing/SM +waxwork/SM +waxy/RPT +way/SM +waybill/SM +wayfarer/MS +wayfaring/SM +waylaid +waylay/RSZG +waylayer/M +wayside/SM +wayward/PY +waywardness/M +wazoo/S +we +we'd +we'll +we're +we've +weak/PNRYXT +weaken/DRZG +weakener/M +weakfish/MS +weakish +weakling/SM +weakness/MS +weal/MHS +wealth/M +wealthiness/M +wealthy/TRP +wean/DGS +weapon/MS +weaponize/GDS +weaponless +weaponry/M +wear/MRBJSZG +wearable/U +wearer/M +wearied/U +wearily +weariness/M +wearisome/Y +weary/TGDRSP +weasel/MDYSG +weather/SMDG +weatherboard/SG +weathercock/MS +weathering/M +weatherization/M +weatherize/DSG +weatherman/M +weathermen +weatherperson/MS +weatherproof/GSD +weatherstrip/S +weatherstripped +weatherstripping/M +weave/DRSMZG +weaver/M +weaving/M +web/SM +webbed +webbing/M +webcam/MS +webcast/SMG +webfeet +webfoot/M +webinar/SM +webisode/MS +weblog/MS +webmaster/SM +webmistress/MS +website/SM +wed/AS +wedded/A +wedder +wedding/SM +wedge/DSMG +wedgie/MS +wedlock/M +wee/RSMT +weed/MDRSZG +weeder/M +weedkiller/S +weedless +weedy/TR +weeing +week/MYS +weekday/SM +weekend/SZGMDR +weekly/SM +weeknight/SM +ween/DSG +weenie/MTRS +weensy/RT +weeny +weep/MRJSZG +weeper/M +weepie +weepy/TRSM +weevil/MS +weft/MS +weigh's +weigh/AGD +weighbridge/S +weighs/A +weight/MDSJG +weighted/U +weightily +weightiness/M +weightless/YP +weightlessness/M +weightlifter/MS +weightlifting/M +weighty/PTR +weir/MS +weird/PTRY +weirdie/MS +weirdness/M +weirdo/MS +welcome/MGDS +weld/MDRBSZG +welder/M +welfare/M +welkin/M +well/MDPSG +wellhead/SM +wellie +wellington/MS +wellness/M +wellspring/MS +welly/S +welsh/ZGDRS +welsher/M +welt/MDRSZG +welter/GMD +welterweight/SM +wen/M +wench/MS +wend/DSG +went +wept +were +weren't +werewolf/M +werewolves +west/M +westbound +westerly/SM +western/SZMR +westerner/M +westernization/M +westernize/GDS +westernmost +westward/S +wet/SMYP +wetback/SM +wetland/SM +wetness/M +wetter/SM +wettest +wetting +wetware/S +whack/SJZGMDR +whacker/M +whale/DRSMZG +whaleboat/MS +whalebone/M +whaler/M +whaling/M +wham/MS +whammed +whamming +whammy/SM +wharf/M +wharves +what/MS +whatchamacallit/MS +whatever +whatnot/M +whatshername +whatshisname +whatsit/S +whatsoever +wheal/SM +wheat/MN +wheatgerm +wheatmeal +whee +wheedle/DRSZG +wheedler/M +wheel/SMDRG +wheelbarrow/SM +wheelbase/SM +wheelchair/SM +wheelhouse/MS +wheelie/SM +wheelwright/MS +wheeze/DSMG +wheezily +wheeziness/M +wheezy/PRT +whelk/SMD +whelm/SDG +whelp/SMDG +when/MS +whence +whenever +whensoever +where/SM +whereabouts/M +whereas +whereat +whereby +wherefore/MS +wherein +whereof +whereon +wheresoever +whereto +whereupon +wherever +wherewith +wherewithal/M +wherry/SM +whet/S +whether +whetstone/SM +whetted +whetting +whew +whey/M +which +whichever +whiff/SMDG +whiffletree/MS +while/DSMG +whilom +whilst +whim/MS +whimper/MDGS +whimsical/Y +whimsicality/M +whimsy/SM +whine/DRSMZG +whiner/M +whinge/DRSZG +whingeing +whinny/GDSM +whiny/RT +whip/MS +whipcord/M +whiplash/MS +whipped +whipper/MS +whippersnapper/MS +whippet/MS +whipping/SM +whippletree/SM +whippoorwill/MS +whipsaw/MDGS +whir/MS +whirl/SMDG +whirligig/MS +whirlpool/MS +whirlwind/MS +whirlybird/SM +whirred +whirring +whisk/SMDRZG +whisker/MD +whiskery +whiskey/MS +whiskys +whisper/MDRSZG +whisperer/M +whist/M +whistle/MZGDRS +whistler/M +whit/MDNRSXTGJ +white/SPM +whitebait +whiteboard/S +whitecap/SM +whitefish/MS +whitehead/MS +whitelist/GDS +whiten/ZGDRJ +whitener/M +whiteness/M +whitening/M +whiteout/SM +whitetail/MS +whitewall/SM +whitewash/MDSG +whitewater/M +whitey/SM +whither +whiting/M +whitish +whittle/ZGDRS +whittler/M +whiz/M +whizkid/M +whizzbang/MS +whizzed +whizzes +whizzing +who'd +who'll +who're +who've +who/M +whoa +whodunit/MS +whoever +whole/SMP +wholefood/S +wholegrain +wholehearted/YP +wholeheartedness/M +wholemeal +wholeness/M +wholesale/MZGDRS +wholesaler/M +wholesome/UP +wholesomely +wholesomeness/UM +wholewheat +wholly +whom +whomever +whomsoever +whoop/SMDRZG +whoopee/S +whooper/M +whoosh/MDSG +whop/S +whopped +whopper/SM +whopping +whore/SMG +whorehouse/MS +whoreish +whorish +whorl/SMD +whose +whoso +whosoever +whup/S +whupped +whupping +why'd +why/M +whys +wick/MDRSZ +wicked/TPRY +wickedness/M +wicker/M +wickerwork/M +wicket/SM +wide/YTRP +widemouthed +widen/SDRZG +widener/M +wideness/M +widescreen/MS +widespread +widget/S +widow/SMDRZG +widower/M +widowhood/M +width/M +widths +wield/SDRZG +wielder/M +wiener/SM +wienie/SM +wife/MY +wifeless +wig/SM +wigeon/M +wigged +wigging +wiggle/DRSMZG +wiggler/M +wiggly/TR +wight/SM +wiglet/SM +wigwag/SM +wigwagged +wigwagging +wigwam/SM +wiki/MS +wild/MRYSTP +wildcard/MS +wildcat/MS +wildcatted +wildcatter/MS +wildcatting +wildebeest/MS +wilderness/MS +wildfire/MS +wildflower/SM +wildfowl/M +wildlife/M +wildness/M +wilds/M +wile/MGDS +wiliness/M +will/MDS +willful/PY +willfulness/M +willies/M +willing/UPY +willingness/UM +williwaw/MS +willow/SM +willowy +willpower/M +willy/S +wilt/MDSG +wily/RTP +wimp/MDSG +wimpish +wimple/DSMG +wimpy/RT +win/SGMD +wince/DSMG +winch/MDSG +wind's +wind/UASG +windbag/SM +windblown +windbreak/SZMR +windbreaker/M +windburn/MD +windcheater/S +windchill/M +winded +winder/SM +windfall/MS +windflower/MS +windily +windiness/M +winding's +windjammer/SM +windlass/MS +windless +windmill/MDGS +window/SMDG +windowless +windowpane/SM +windowsill/SM +windpipe/MS +windproof +windrow/SM +windscreen/SM +windshield/SM +windsock/MS +windstorm/MS +windsurf/ZGDRS +windsurfer/M +windsurfing/M +windswept +windup/SM +windward/M +windy/RTP +wine/MS +wineglass/MS +winegrower/MS +winemaker/MS +winery/SM +wing/MDRZG +wingding/MS +wingless +winglike +wingnut/SM +wingspan/MS +wingspread/SM +wingtip/SM +wink/MDRSZG +winker/M +winkle/DSMG +winnable/U +winner/SM +winning/MYS +winnow/ZGSDR +winnower/M +wino/MS +winsome/YTRP +winsomeness/M +winter/GSMD +wintergreen/M +winterize/GDS +wintertime/M +wintry/TR +winy/RT +wipe/MZGDRS +wiper/M +wire's +wire/AGDS +wired/S +wirehair/MS +wireless/MS +wiretap/MS +wiretapped +wiretapper/SM +wiretapping/M +wiriness/M +wiring/M +wiry/RTP +wisdom/M +wise/MYTGDRS +wiseacre/SM +wisecrack/MDSG +wiseguy/S +wish/MDRSZG +wishbone/SM +wisher/M +wishful/Y +wishlist's +wisp/MS +wispy/RT +wist +wisteria/SM +wistful/YP +wistfulness/M +wit/SM +witch/MDSG +witchcraft/M +witchery/M +with +withal +withdraw/SG +withdrawal/MS +withdrawn +withdrew +withe/DRSMZG +wither/JGD +withering/Y +withers/M +withheld +withhold/SG +withholding/M +within/M +without +withstand/GS +withstood +witless/PY +witlessness/M +witness/MDSG +wits/M +witted +witter/SGD +witticism/SM +wittily +wittiness/M +witting/UY +witty/RPT +wive/GDS +wiz +wizard/SMY +wizardry/M +wizened +wk/Y +woad/M +wobble/MGDS +wobbliness/M +wobbly/RTP +wodge/S +woe/SM +woebegone +woeful/YP +woefuller +woefullest +woefulness/M +wog/S +wok/SMN +woke +wold/MS +wolf/MDSG +wolfhound/SM +wolfish +wolfram/M +wolverine/SM +wolves +woman/M +womanhood/M +womanish +womanize/DRSZG +womanizer/M +womankind/M +womanlike/M +womanliness/M +womanly/RPT +womb/MS +wombat/MS +womble/S +women/M +womenfolk/SM +womenfolks/M +won't +won/M +wonder/MDGLS +wonderful/YP +wonderfulness/M +wondering/Y +wonderland/MS +wonderment/M +wondrous/Y +wonk/MS +wonky/TR +wont/MD +wonted/U +woo/SZGDR +wood/MDNSG +woodbine/M +woodblock/MS +woodcarver/MS +woodcarving/SM +woodchuck/MS +woodcock/SM +woodcraft/M +woodcut/SM +woodcutter/SM +woodcutting/M +wooden/RYTP +woodenness/M +woodiness/M +woodland/SM +woodlice +woodlot/SM +woodlouse +woodman/M +woodmen +woodpecker/MS +woodpile/SM +woods/M +woodshed/SM +woodsiness/M +woodsman/M +woodsmen +woodsy/RTP +woodwind/MS +woodwork/MRZG +woodworker/M +woodworking/M +woodworm/S +woody/TPRSM +wooer/M +woof/MDRSZG +woofer/M +wool/MNX +woolen/M +woolgathering/M +wooliness +woolliness/M +woolly/RSMPT +woozily +wooziness/M +woozy/TRP +wop/S! +word's +word/ADSG +wordage/M +wordbook/SM +wordily +wordiness/M +wording/SM +wordless/Y +wordplay/M +wordsmith +wordsmiths +wordy/TPR +wore +work's +work/ADJSG +workable/U +workaday +workaholic/SM +workaround/S +workbasket/S +workbench/MS +workbook/MS +workday/SM +worker/MS +workfare/M +workflow/MS +workforce/M +workhorse/SM +workhouse/SM +working's +workingman/M +workingmen +workings/M +workingwoman/M +workingwomen +workload/MS +workman/M +workmanlike +workmanship/M +workmate/S +workmen +workout/SM +workplace/MS +workroom/MS +works/M +worksheet/MS +workshop/MS +workshy +worksite/S +workspace +workstation/MS +worktable/MS +worktop/S +workup/MS +workweek/SM +world/SM +worldlier +worldliness/UM +worldly/UTP +worldview/SM +worldwide +worm/MDSG +wormhole/MS +wormwood/M +wormy/TR +worn/U +worried/Y +worrier/M +worriment/M +worrisome +worry/ZGDRSMJ +worrying/Y +worrywart/SM +worse/M +worsen/DSG +worship/ZGSMDR +worshiper/M +worshipful +worst/SGMD +worsted/M +wort/M +worth/M +worthies +worthily/U +worthiness/UM +worthless/PY +worthlessness/M +worthwhile +worthy's +worthy/UPRT +wot +wotcha +would've +would/S +wouldn't +wouldst +wound/SGMDR +wove/A +woven/AU +wow/SGMD +wpm +wrack/GSMD +wraith/M +wraiths +wrangle/DRSMZGJ +wrangler/M +wrap's +wrap/US +wraparound/SM +wrapped/U +wrapper/SM +wrapping/MS +wrasse/MS +wrath/M +wrathful/Y +wreak/SGD +wreath/MDSG +wreathe +wreaths +wreck/SZGMDR +wreckage/M +wrecker/M +wren/MS +wrench/MDSG +wrest/SGMD +wrestle/MZGDRS +wrestler/M +wrestling/M +wretch/MS +wretched/TPRY +wretchedness/M +wriggle/MZGDRS +wriggler/M +wriggly +wright/MS +wring/SZGMR +wringer/M +wrinkle/MGDS +wrinkled/U +wrinkly/TRSM +wrist/SM +wristband/MS +wristwatch/MS +writ/MRBJSZG +write/S +writer/M +writhe/MGDS +writing/M +written/AU +wrong/STGMPDRY +wrongdoer/SM +wrongdoing/SM +wrongful/PY +wrongfulness/M +wrongheaded/YP +wrongheadedness/M +wrongness/M +wrote/A +wroth +wrought +wrung +wry/Y +wryer +wryest +wryness/M +wt +wunderkind/S +wurst/SM +wuss/MS +wussy/RSMT +x +xci +xcii +xciv +xcix +xcvi +xcvii +xenon/M +xenophobe/MS +xenophobia/M +xenophobic +xerographic +xerography/M +xerox/MDSG +xi/SM +xii +xiii +xiv +xix +xor +xref/S +xterm/M +xv +xvi +xvii +xviii +xx +xxi +xxii +xxiii +xxiv +xxix +xxv +xxvi +xxvii +xxviii +xxx +xxxi +xxxii +xxxiii +xxxiv +xxxix +xxxv +xxxvi +xxxvii +xxxviii +xylem/M +xylene +xylophone/SM +xylophonist/MS +y'all +y/F +ya +yacht/SMDG +yachting/M +yachtsman/M +yachtsmen +yachtswoman/M +yachtswomen +yahoo/SM +yak/SM +yakked +yakking +yam/SM +yammer/SZGMDR +yammerer/M +yang/M +yank/MDSG +yap/SM +yapped +yapping +yard/MS +yardage/MS +yardarm/MS +yardman/M +yardmaster/MS +yardmen +yardstick/MS +yarmulke/SM +yarn/MS +yarrow/M +yashmak/S +yaw/SGMD +yawl/MS +yawn/MDRSZG +yawner/M +yaws/M +yd +ye/RST +yea/SM +yeah/M +yeahs +year/MYS +yearbook/MS +yearling/MS +yearlong +yearly/SM +yearn/GSJD +yearning/M +yeast/SM +yeasty/RT +yegg/MS +yell/MDSG +yellow/MDRTGPS +yellowhammer/S +yellowish +yellowness/M +yellowy +yelp/MDSG +yen/SM +yeoman/M +yeomanry/M +yeomen +yep/SM +yes/MS +yeshiva/SM +yessed +yessing +yesterday/MS +yesteryear/M +yet +yeti/MS +yew/SM +yid/S +yield/JSGMD +yikes +yin/M +yip/SM +yipe +yipped +yippee +yipping +yo +yob/S +yobbo/S +yodel/SMDRZG +yodeler/M +yoga/M +yogi/MS +yogic +yogurt/SM +yoke's +yoke/UGDS +yokel/SM +yolk/MDS +yon +yonder +yonks +yore/M +you'd +you'll +you're +you've +you/SMH +young/TMR +youngish +youngster/MS +your/S +yourself +yourselves +youth/M +youthful/YP +youthfulness/M +youths +yow +yowl/MDSG +yr/S +ytterbium/M +yttrium/M +yuan/M +yucca/SM +yuck +yucky/TR +yuk/SM +yukked +yukking +yukky +yule/M +yuletide/M +yum +yummy/TR +yup/SM +yuppie/MS +yuppify/GDS +yurt/MS +z/DNXTGJ +zaniness/M +zany/RSMPT +zap/SM +zapped +zapper/MS +zapping +zappy +zeal/M +zealot/MS +zealotry/M +zealous/YP +zealousness/M +zebra/SM +zebu/MS +zed/SM +zeitgeist/SM +zenith/M +zeniths +zenned +zeolite/S +zephyr/MS +zeppelin/MS +zero/MDHSG +zeroes +zest/MS +zestful/YP +zestfulness/M +zesty/RT +zeta/MS +zigzag/SM +zigzagged +zigzagging +zilch/M +zillion/MS +zinc/MS +zincked +zincking +zine/S +zinfandel/M +zing/MDRZG +zinger/M +zingy/RT +zinnia/MS +zip's +zip/US +zipped/U +zipper/MDGS +zipping/U +zippy/TR +zircon/MS +zirconium/M +zit/SM +zither/MS +zloty/SM +zlotys +zodiac/MS +zodiacal +zombie/MS +zonal/Y +zone's +zone/AGDS +zoning/M +zonked +zoo/SM +zookeeper/SM +zoological/Y +zoologist/SM +zoology/M +zoom/MDSG +zoophyte/SM +zoophytic +zooplankton +zorch +zoster +zounds +zucchini/MS +zwieback/M +zydeco/M +zygote/SM +zygotic +zymurgy/M diff --git a/crates/ide-spell/src/dict/software.txt b/crates/ide-spell/src/dict/software.txt new file mode 100644 index 00000000..0a92977c --- /dev/null +++ b/crates/ide-spell/src/dict/software.txt @@ -0,0 +1,463 @@ +# Programming terms the en_US dictionary lacks (IDEA bundles a similar "software terms" list). +# One lowercase word per line; checked case-insensitively. +abi +accessor +accessors +addr +admin +aggregator +alloc +allocator +amd +analytics +api +apis +app +apps +arg +argc +args +argv +arity +arm +async +atomics +attr +attrs +auth +authn +authz +autocomplete +autoload +backend +backends +backoff +backtrace +bitfield +bitflags +bitmask +bitset +bitwise +blob +blobs +bool +boolean +booleans +borrowck +bundler +buf +bufs +builtin +builtins +bytecode +cacheable +callback +callbacks +callee +callees +callsite +cfg +changelog +changeset +charset +checkbox +checksum +chmod +chown +cli +codebase +codec +codegen +codepoint +codepoints +color +colors +colorize +combinator +combinators +commandline +config +configs +configurable +const +consts +constexpr +coroutine +coroutines +cpu +cpus +crate +crates +css +csv +ctor +ctx +ctrl +cwd +daemon +dao +datagram +dataset +datasource +datatype +datetime +db +dbg +dealloc +debounce +debounced +debouncer +decl +decls +decrement +dedup +dedupe +deduplicate +deinit +del +delim +deps +deque +deref +dereference +deserialization +deserialize +deserialized +deserializer +desc +dest +dev +devs +dict +dicts +diff +diffs +dir +dirs +dirname +dll +dns +docstring +docstrings +dom +dtor +dto +dyn +elem +elems +enum +enums +env +envs +eof +eol +eq +errno +esc +eval +exe +expr +exprs +ext +failover +fallback +fallthrough +favicon +fd +fds +fifo +filename +filenames +filepath +filesystem +filesystems +filetype +fixme +fmt +fn +fns +foo +bar +baz +qux +frontend +fs +func +funcs +gc +getter +getters +gettext +github +gitignore +glob +globals +globbing +goroutine +goroutines +gpu +grpc +gui +gzip +hardcode +hardcoded +hashable +hashmap +hashset +hasher +heapify +hex +hostname +href +html +http +https +hunspell +iframe +ident +idx +impl +impls +init +inits +initializer +inline +inlined +inlining +int +ints +io +iter +iterable +iterables +iters +javadoc +jit +js +json +jsx +jwt +kotlin +kwargs +lambda +lambdas +len +lexer +lhs +lib +libc +libs +linter +localhost +lookahead +lookbehind +lookup +lookups +lsp +lvalue +malloc +markdown +memcpy +memoize +memoized +metadata +middleware +minified +minify +mixin +mixins +mmap +modal +monorepo +msg +msgs +multiline +mut +mutex +mutexes +namespace +namespaced +namespaces +nav +nginx +noop +npm +nullable +num +nums +oauth +obj +objs +offscreen +ok +opcode +opcodes +os +param +params +parsable +parser +parsers +passthrough +pathname +pid +pids +pixmap +placeholder +plugin +plugins +png +popup +popups +postfix +pre +precompute +precomputed +prefetch +preload +prepend +prepended +printf +println +proc +procs +protobuf +proxied +ptr +ptrs +pubsub +py +quickfix +rect +rects +redis +refactor +refactored +refactoring +refactorings +regex +regexes +regexp +renderer +repl +repo +repos +req +res +resize +resizable +resolver +resp +rhs +rpc +runnable +runtime +runtimes +rustc +rustfmt +rvalue +scalability +schemas +scrollbar +sdk +sdks +semver +serializable +serialization +serializer +setter +setters +sha +shader +signup +sizeof +smtp +sql +sqlite +src +ssh +ssl +stacktrace +stderr +stdin +stdio +stdlib +stdout +str +strs +struct +structs +subclass +subclasses +subcommand +subcommands +subdirectory +subdirectories +submodule +submodules +subprocess +subtree +svg +symlink +symlinks +sync +syscall +syscalls +tcp +templating +termination +timestamp +timestamps +tls +tmp +todo +todos +tokenize +tokenized +tokenizer +toml +toolchain +toolchains +toolbar +tooltip +tooltips +tsx +tuple +tuples +txt +typeof +ui +uid +uint +unary +unicode +unix +unmount +unregister +unset +unsubscribe +untracked +uri +uris +url +urls +usize +utf +util +utils +uuid +val +vals +var +vars +vec +vecs +viewport +vm +whitespace +wildcard +workspace +workspaces +xml +yaml +yml diff --git a/crates/ide-spell/src/lib.rs b/crates/ide-spell/src/lib.rs new file mode 100644 index 00000000..f8cb34e0 --- /dev/null +++ b/crates/ide-spell/src/lib.rs @@ -0,0 +1,338 @@ +//! Spell checking for IDEA's "Proofreading › Typo" inspection: words of +//! comments, string literals and declared names (regions come from the +//! syntax layer), checked against the bundled en_US Hunspell dictionary, a +//! list of programming terms and the user's accepted words and dictionaries. + +mod words; + +use ide_core::{Pos, Range}; +use parking_lot::Mutex; +use serde::{Deserialize, Serialize}; +use specta::Type; +use std::collections::{HashMap, HashSet}; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, OnceLock}; +use std::time::SystemTime; + +pub use words::{MIN_WORD_CHARS, WordSpan, words}; + +/// Where a region's words come from; strings and comments understand escapes. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RegionKind { + Comment, + String, + /// A declared identifier (`camelCase` / `snake_case` split). + Name, + /// Plain text / Markdown prose. + Prose, +} + +/// A byte range of the text to check. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Region { + pub start: usize, + pub end: usize, + pub kind: RegionKind, +} + +/// A misspelt word. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] +pub struct Typo { + /// UTF-16 range of the word. + pub range: Range, + pub word: String, +} + +/// At most this many typos are reported per text. +pub const MAX_TYPOS: usize = 2000; + +fn english() -> &'static spellbook::Dictionary { + static DICT: OnceLock = OnceLock::new(); + DICT.get_or_init(|| { + let aff = include_str!("dict/en_US.aff"); + let dic = include_str!("dict/en_US.dic"); + spellbook::Dictionary::new(aff, dic).expect("bundled en_US dictionary parses") + }) +} + +fn software_terms() -> &'static HashSet<&'static str> { + static TERMS: OnceLock> = OnceLock::new(); + TERMS.get_or_init(|| { + include_str!("dict/software.txt") + .lines() + .map(str::trim) + .filter(|l| !l.is_empty() && !l.starts_with('#')) + .collect() + }) +} + +/// Words of a custom dictionary file: a plain word list or a Hunspell `.dic` +/// (leading count line and `/FLAGS` ignored). Cached by modification time. +fn dictionary_file(path: &Path) -> Arc> { + type Cache = HashMap, Arc>)>; + static CACHE: OnceLock> = OnceLock::new(); + let modified = std::fs::metadata(path).and_then(|m| m.modified()).ok(); + let mut cache = CACHE.get_or_init(Default::default).lock(); + if let Some((at, words)) = cache.get(path) + && *at == modified + { + return words.clone(); + } + let words: HashSet = match std::fs::read_to_string(path) { + Ok(text) => parse_word_list(&text), + Err(e) => { + tracing::debug!("custom dictionary {}: {e}", path.display()); + HashSet::new() + } + }; + let words = Arc::new(words); + cache.insert(path.to_path_buf(), (modified, words.clone())); + words +} + +/// Lowercased words of a word list / Hunspell `.dic` text. +pub fn parse_word_list(text: &str) -> HashSet { + let mut lines = text.lines().map(str::trim).peekable(); + if lines.peek().is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit())) { + lines.next(); + } + lines + .filter(|l| !l.is_empty() && !l.starts_with('#')) + .map(|l| l.split('/').next().unwrap_or(l).trim().to_lowercase()) + .filter(|w| !w.is_empty()) + .collect() +} + +/// The dictionary plus the user's accepted words and custom dictionaries. +#[derive(Default)] +pub struct Speller { + accepted: HashSet, + custom: Vec>>, +} + +impl Speller { + pub fn new>(accepted: impl IntoIterator, dictionaries: &[PathBuf]) -> Self { + Self { + accepted: accepted.into_iter().map(|w| w.as_ref().trim().to_lowercase()).collect(), + custom: dictionaries.iter().map(|p| dictionary_file(p)).collect(), + } + } + + /// Whether `word` is spelt correctly. Case is forgiven the way identifiers + /// need it: `url` passes for `URL`, `paris` for `Paris`. + pub fn is_correct(&self, word: &str) -> bool { + let lower = word.to_lowercase(); + if self.accepted.contains(&lower) + || software_terms().contains(lower.as_str()) + || self.custom.iter().any(|d| d.contains(&lower)) + { + return true; + } + let dict = english(); + if dict.check(word) || dict.check(&lower) { + return true; + } + let mut chars = lower.chars(); + let capitalized: String = chars.next().into_iter().flat_map(char::to_uppercase).chain(chars).collect(); + dict.check(&capitalized) || dict.check(&lower.to_uppercase()) + } + + /// Replacements for `word`, best first, in the word's case style. + pub fn suggest(&self, word: &str, limit: usize) -> Vec { + let mut out = Vec::new(); + english().suggest(word, &mut out); + if out.is_empty() && word.chars().next().is_some_and(char::is_uppercase) { + english().suggest(&word.to_lowercase(), &mut out); + out = out.into_iter().map(|s| capitalize(&s)).collect(); + } + let mut seen = HashSet::new(); + out.retain(|s| s != word && seen.insert(s.clone())); + out.truncate(limit); + out + } + + /// Misspelt words of `regions` (document order) in `text`; `None` when + /// `cancelled` fires. + pub fn check(&self, text: &str, regions: &[Region], cancelled: &dyn Fn() -> bool) -> Option> { + let mut spans = Vec::new(); + let mut verdicts: HashMap<&str, bool> = HashMap::new(); + let mut bad: Vec = Vec::new(); + for (i, r) in regions.iter().enumerate() { + if i % 64 == 0 && cancelled() { + return None; + } + spans.clear(); + words(text, r.start, r.end, r.kind, &mut spans); + for w in &spans { + let word = &text[w.start..w.end]; + let ok = *verdicts.entry(word).or_insert_with(|| self.is_correct(word)); + if !ok { + bad.push(*w); + } + } + if bad.len() >= MAX_TYPOS { + bad.truncate(MAX_TYPOS); + break; + } + } + bad.sort_by_key(|w| w.start); + bad.dedup(); + let mut index = Utf16Walker::new(text); + Some( + bad.into_iter() + .map(|w| Typo { + range: Range::new(index.pos(w.start), index.pos(w.end)), + word: text[w.start..w.end].to_string(), + }) + .collect(), + ) + } +} + +fn capitalize(s: &str) -> String { + let mut chars = s.chars(); + chars.next().into_iter().flat_map(char::to_uppercase).chain(chars).collect() +} + +/// Regions of a plain-text / Markdown document: everything but fenced code +/// blocks and inline code spans. +pub fn prose_regions(text: &str) -> Vec { + let mut out = Vec::new(); + let mut fence: Option<&str> = None; + let mut offset = 0; + for line in text.split_inclusive('\n') { + let start = offset; + offset += line.len(); + let trimmed = line.trim_start(); + let marker = ["```", "~~~"].into_iter().find(|m| trimmed.starts_with(m)); + match (fence, marker) { + (Some(open), Some(m)) if m == open => { + fence = None; + continue; + } + (Some(_), _) => continue, + (None, Some(m)) => { + fence = Some(m); + continue; + } + (None, None) => {} + } + // Text between backtick pairs is code. + let mut in_code = false; + let mut seg = start; + for (i, c) in line.char_indices() { + if c == '`' { + if !in_code && seg < start + i { + out.push(Region { start: seg, end: start + i, kind: RegionKind::Prose }); + } + in_code = !in_code; + seg = start + i + 1; + } + } + if !in_code && seg < offset { + out.push(Region { start: seg, end: offset, kind: RegionKind::Prose }); + } + } + out +} + +/// Byte offset → line / UTF-16 column, for increasing offsets. +struct Utf16Walker<'a> { + text: &'a str, + byte: usize, + pos: Pos, +} + +impl<'a> Utf16Walker<'a> { + fn new(text: &'a str) -> Self { + Self { text, byte: 0, pos: Pos::new(0, 0) } + } + + fn pos(&mut self, target: usize) -> Pos { + if target < self.byte { + *self = Self::new(self.text); + } + for c in self.text[self.byte..target].chars() { + if c == '\n' { + self.pos = Pos::new(self.pos.line + 1, 0); + } else { + self.pos.col += c.len_utf16() as u32; + } + } + self.byte = target; + self.pos + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn whole(text: &str, kind: RegionKind) -> Vec { + vec![Region { start: 0, end: text.len(), kind }] + } + + #[test] + fn flags_misspellings_only() { + let s = Speller::default(); + let text = "// Recieve the mesage from the server's backend"; + let typos = s.check(text, &whole(text, RegionKind::Comment), &|| false).unwrap(); + let words: Vec<_> = typos.iter().map(|t| t.word.as_str()).collect(); + assert_eq!(words, ["Recieve", "mesage"]); + assert_eq!(typos[0].range, Range::new(Pos::new(0, 3), Pos::new(0, 10))); + } + + #[test] + fn identifiers_forgive_case_and_terms() { + let s = Speller::default(); + for ok in ["url", "Paris", "json", "parser", "println"] { + assert!(s.is_correct(ok), "{ok}"); + } + assert!(!s.is_correct("lenght")); + } + + #[test] + fn accepted_words_and_custom_dictionaries() { + let dir = std::env::temp_dir().join(format!("ide-spell-{}", std::process::id())); + std::fs::create_dir_all(&dir).unwrap(); + let dic = dir.join("team.dic"); + std::fs::write(&dic, "2\nfrobnicate/S\nQuuxify\n").unwrap(); + let s = Speller::new(["Openidea"], std::slice::from_ref(&dic)); + assert!(s.is_correct("openidea")); + assert!(s.is_correct("frobnicate")); + assert!(s.is_correct("quuxify")); + assert!(!s.is_correct("zorblax")); + let _ = std::fs::remove_dir_all(dir); + } + + #[test] + fn utf16_ranges() { + let s = Speller::default(); + let text = "// 中文 é\n// wrng"; + let typos = s.check(text, &whole(text, RegionKind::Comment), &|| false).unwrap(); + assert_eq!(typos.len(), 1); + assert_eq!(typos[0].range, Range::new(Pos::new(1, 3), Pos::new(1, 7))); + } + + #[test] + fn suggestions() { + let s = Speller::default(); + let out = s.suggest("mesage", 5); + assert!(out.iter().any(|w| w == "message"), "{out:?}"); + assert!(s.suggest("Recieve", 5).iter().any(|w| w == "Receive")); + } + + #[test] + fn markdown_skips_code() { + let text = "Some prose `codez` here\n```\nfn wrng() {}\n```\nmore\n"; + let regions = prose_regions(text); + let spans: Vec<_> = regions.iter().map(|r| &text[r.start..r.end]).collect(); + assert_eq!(spans, ["Some prose ", " here\n", "more\n"]); + } + + #[test] + fn cancellation() { + let s = Speller::default(); + assert!(s.check("x", &whole("x", RegionKind::Comment), &|| true).is_none()); + } +} diff --git a/crates/ide-spell/src/words.rs b/crates/ide-spell/src/words.rs new file mode 100644 index 00000000..430fb877 --- /dev/null +++ b/crates/ide-spell/src/words.rs @@ -0,0 +1,171 @@ +//! Words worth spell checking in a region of source text, IDEA style: +//! identifiers split at `camelCase` / `snake_case` boundaries; URLs, e-mail +//! addresses, escapes, hex, acronyms, short tokens and tokens with digits skipped. + +use crate::RegionKind; + +/// Shorter words are never checked. +pub const MIN_WORD_CHARS: usize = 3; + +/// Byte range of a word in the text the region came from. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct WordSpan { + pub start: usize, + pub end: usize, +} + +/// Words of `text[start..end]` in order, as absolute byte ranges. +pub fn words(text: &str, start: usize, end: usize, kind: RegionKind, out: &mut Vec) { + let Some(slice) = text.get(start..end) else { return }; + let escapes = !matches!(kind, RegionKind::Name); + let mut chunk_start = None; + for (i, c) in slice.char_indices().chain(std::iter::once((slice.len(), ' '))) { + if c.is_whitespace() { + if let Some(s) = chunk_start.take() { + chunk(slice, s, i, escapes, start, out); + } + } else if chunk_start.is_none() { + chunk_start = Some(i); + } + } +} + +/// URLs, e-mail addresses and the like are not prose. +fn skip_chunk(chunk: &str) -> bool { + if chunk.contains("://") || chunk.starts_with("www.") { + return true; + } + match chunk.find('@') { + Some(at) => chunk[at + 1..].contains('.') && at > 0, + None => false, + } +} + +fn chunk(slice: &str, from: usize, to: usize, escapes: bool, base: usize, out: &mut Vec) { + let text = &slice[from..to]; + if skip_chunk(text) { + return; + } + let mut run: Option = None; + let mut chars = text.char_indices().peekable(); + while let Some((i, c)) = chars.next() { + let word_char = c.is_alphanumeric() || c == '_' || c == '\''; + if word_char { + run.get_or_insert(i); + continue; + } + if let Some(s) = run.take() { + token(text, s, i, base + from, out); + } + // `\n`, `\t`, `é`: the escape letter is not part of the next word. + if escapes && c == '\\' { + chars.next(); + } + } + if let Some(s) = run { + token(text, s, text.len(), base + from, out); + } +} + +/// One alphanumeric run (`parseHTMLDoc`, `max_len`, `don't`). +fn token(text: &str, mut s: usize, mut e: usize, base: usize, out: &mut Vec) { + let trim = |c: char| c == '\'' || c == '_'; + while s < e && text[s..e].starts_with(trim) { + s += 1; + } + while e > s && text[s..e].ends_with(trim) { + e -= 1; + } + let tok = &text[s..e]; + if tok.is_empty() || tok.chars().any(|c| c.is_ascii_digit()) { + return; + } + if tok.len() >= 6 && tok.chars().all(|c| c.is_ascii_hexdigit()) { + return; + } + for (ps, pe) in pieces(tok) { + let w = &tok[ps..pe]; + let w = w.trim_matches('\''); + if w.is_empty() { + continue; + } + let ws = s + ps + tok[ps..pe].find(w).unwrap_or(0); + if checkable(w) { + out.push(WordSpan { start: base + ws, end: base + ws + w.len() }); + } + } +} + +/// `snake_case` and `camelCase` / `HTTPServer` pieces of a token. +fn pieces(tok: &str) -> Vec<(usize, usize)> { + let mut out = Vec::new(); + let mut offset = 0; + for part in tok.split('_') { + let chars: Vec<(usize, char)> = part.char_indices().collect(); + let mut start = 0; + for k in 1..chars.len() { + let (i, c) = chars[k]; + let prev = chars[k - 1].1; + let next_lower = chars.get(k + 1).is_some_and(|&(_, n)| n.is_lowercase()); + let boundary = + (prev.is_lowercase() && c.is_uppercase()) || (prev.is_uppercase() && c.is_uppercase() && next_lower); + if boundary { + out.push((offset + start, offset + i)); + start = i; + } + } + if !part.is_empty() { + out.push((offset + start, offset + part.len())); + } + offset += part.len() + 1; + } + out +} + +/// Long enough, not an acronym, and in a script the bundled dictionary covers. +fn checkable(w: &str) -> bool { + let letters = w.chars().filter(|c| c.is_alphabetic()).count(); + if letters < MIN_WORD_CHARS { + return false; + } + if !w.chars().any(|c| c.is_lowercase()) { + return false; + } + // Latin letters only (through Latin Extended-B): CJK, Cyrillic, … are not checked. + w.chars().all(|c| c == '\'' || (c.is_alphabetic() && (c as u32) < 0x250)) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn split(text: &str, kind: RegionKind) -> Vec<&str> { + let mut out = Vec::new(); + words(text, 0, text.len(), kind, &mut out); + out.into_iter().map(|w| &text[w.start..w.end]).collect() + } + + #[test] + fn splits_identifiers() { + assert_eq!(split("parseHTMLDocument", RegionKind::Name), ["parse", "Document"]); + assert_eq!(split("max_line_length", RegionKind::Name), ["max", "line", "length"]); + assert_eq!(split("getUserName", RegionKind::Name), ["get", "User", "Name"]); + assert_eq!(split("XMLHttpRequest", RegionKind::Name), ["Http", "Request"]); + } + + #[test] + fn skips_noise() { + assert_eq!( + split("see https://exmple.com/foo or mail me@exmple.org, id 0xdeadbeef v2beta", RegionKind::Comment), + ["see", "mail"] + ); + assert_eq!(split("deadbeefcafe NASA ok", RegionKind::Comment), Vec::<&str>::new()); + assert_eq!(split("\"line\\nbreak\\twith tab\"", RegionKind::String), ["line", "break", "with", "tab"]); + assert_eq!(split("// 中文注释 naïve", RegionKind::Comment), ["naïve"]); + } + + #[test] + fn keeps_apostrophes_inside_words() { + assert_eq!(split("'don't' shouldn't", RegionKind::Comment), ["don't", "shouldn't"]); + } +} diff --git a/crates/ide-vcs/src/history.rs b/crates/ide-vcs/src/history.rs index 6c007448..5d0c9e78 100644 --- a/crates/ide-vcs/src/history.rs +++ b/crates/ide-vcs/src/history.rs @@ -1,7 +1,7 @@ //! Commit history (topological walk) and per-commit changed files via gitoxide. use crate::repo::{gx, resolve, to_string}; -use crate::{ChangeKind, ChangedFile, Commit, LogQuery, Repo}; +use crate::{ChangeKind, ChangedFile, Commit, LogOrder, LogQuery, Repo}; use gix::ObjectId; use gix::bstr::ByteSlice; use ide_core::{IdeError, IdeResult}; @@ -72,6 +72,7 @@ pub(crate) fn log(r: &Repo, q: &LogQuery) -> IdeResult> { let limit = if q.limit == 0 { usize::MAX } else { q.limit as usize }; let mut to_skip = q.skip as usize; + let path_walk = path_filter.is_some(); type Walk<'a> = Box)>> + 'a>; let walk: Walk<'_> = if let Some(range) = range { let ids = range_ids(r, range, path_filter.as_deref())?; @@ -81,29 +82,64 @@ pub(crate) fn log(r: &Repo, q: &LogQuery) -> IdeResult> { Ok((id, commit.parent_ids().map(|p| p.detach()).collect())) })) } else if let Some(rel) = path_filter { - Box::new(PathWalk::new(&repo, tips, rel)?) + Box::new(PathWalk::new(&repo, tips, rel, q.first_parent)?) } else if repo.is_shallow().unwrap_or(false) { // Topological order needs complete history; shallow clones fall back to date order. - let walk = repo - .rev_walk(tips) - .sorting(gix::revision::walk::Sorting::ByCommitTime(Default::default())) - .all() - .map_err(gx)?; + let mut walk = repo.rev_walk(tips).sorting(gix::revision::walk::Sorting::ByCommitTime(Default::default())); + if q.first_parent { + walk = walk.first_parent_only(); + } + let walk = walk.all().map_err(gx)?; Box::new(walk.map(|info| info.map(|i| (i.id, i.parent_ids.to_vec())).map_err(gx))) } else { + use gix::traverse::commit::{Parents, topo}; let graph = repo.commit_graph_if_enabled().ok().flatten(); - let topo = gix::traverse::commit::topo::Builder::from_iters(&repo.objects, tips, None::>) - .sorting(gix::traverse::commit::topo::Sorting::TopoOrder) + let sorting = match q.order { + LogOrder::Topo => topo::Sorting::TopoOrder, + LogOrder::Date => topo::Sorting::DateOrder, + }; + let topo = topo::Builder::from_iters(&repo.objects, tips, None::>) + .sorting(sorting) + .parents(if q.first_parent { Parents::First } else { Parents::All }) .with_commit_graph(graph) .build() .map_err(gx)?; Box::new(topo.map(|info| info.map(|i| (i.id, i.parent_ids.to_vec())).map_err(gx))) }; + let shape = Shape { + no_merges: q.no_merges, + first_parent: q.first_parent, + since: q.since_ms.map(|ms| (ms / 1000.0).floor() as i64), + until: q.until_ms.map(|ms| (ms / 1000.0).ceil() as i64), + }; + // Walks in (roughly) commit time order can stop once they are well past `since`. + let time_ordered = range.is_none() && (q.order == LogOrder::Date || path_walk); + let mut older_run = 0usize; + let mut out = Vec::new(); for item in walk { - let (id, parents) = item?; + let (id, mut parents) = item?; + if q.first_parent { + parents.truncate(1); + } let commit = repo.find_commit(id).map_err(gx)?; + let commit_time = commit.time().map(|t| t.seconds).unwrap_or(0); + if shape.is_active() { + let merge = commit.parent_ids().nth(1).is_some(); + if shape.since.is_some_and(|s| commit_time < s) { + older_run += 1; + if time_ordered && older_run >= OLDER_RUN_LIMIT { + break; + } + continue; + } + older_run = 0; + if !shape.shows(merge, commit_time) { + continue; + } + parents = shape.rewrite_parents(&repo, &parents)?; + } let sig = commit.author().map_err(gx)?; let name = to_string(sig.name.trim().as_bstr()); let email = to_string(sig.email.trim().as_bstr()); @@ -136,6 +172,7 @@ pub(crate) fn log(r: &Repo, q: &LogQuery) -> IdeResult> { summary, message: message.trim_end().to_string(), refs: decorations.get(&id).cloned().unwrap_or_default(), + commit_time_ms: commit_time as f64 * 1000.0, }); if out.len() >= limit { break; @@ -144,6 +181,66 @@ pub(crate) fn log(r: &Repo, q: &LogQuery) -> IdeResult> { Ok(out) } +/// Consecutive commits older than `since` after which a time-ordered walk stops. +const OLDER_RUN_LIMIT: usize = 64; + +/// Which commits the Log graph shows independently of the detail filters (author, text): +/// `--no-merges` and the date range. Hidden commits are skipped and the parents of shown +/// ones rewritten past them, like git's parent rewriting. +struct Shape { + no_merges: bool, + first_parent: bool, + /// Commit time bounds in seconds; `since` inclusive, `until` exclusive. + since: Option, + until: Option, +} + +/// Upper bound of commits visited while rewriting one commit's parents. +const REWRITE_LIMIT: usize = 4096; + +impl Shape { + fn is_active(&self) -> bool { + self.no_merges || self.since.is_some() || self.until.is_some() + } + + fn shows(&self, merge: bool, time: i64) -> bool { + !(self.no_merges && merge) && self.since.is_none_or(|s| time >= s) && self.until.is_none_or(|u| time < u) + } + + /// `parents` with each hidden one replaced by its nearest shown ancestors; parents + /// older than `since` are dropped (their history is not shown). + fn rewrite_parents(&self, repo: &gix::Repository, parents: &[ObjectId]) -> IdeResult> { + let mut out: Vec = Vec::new(); + let mut seen: HashSet = HashSet::new(); + let mut stack: Vec = parents.iter().rev().copied().collect(); + let mut visited = 0usize; + while let Some(p) = stack.pop() { + if !seen.insert(p) || !repo.has_object(p) { + continue; + } + visited += 1; + if visited > REWRITE_LIMIT { + break; + } + let commit = repo.find_commit(p).map_err(gx)?; + let time = commit.time().map(|t| t.seconds).unwrap_or(0); + if self.since.is_some_and(|s| time < s) { + continue; + } + let ids: Vec = commit.parent_ids().map(|i| i.detach()).collect(); + if self.shows(ids.len() > 1, time) { + if !out.contains(&p) { + out.push(p); + } + continue; + } + let next = if self.first_parent { &ids[..ids.len().min(1)] } else { &ids[..] }; + stack.extend(next.iter().rev()); + } + Ok(out) + } +} + /// Commit ids of a revision range (`A..B`, `A...B`, `^A B`…), topologically ordered, via /// `git rev-list` so every rev syntax git understands (`@{u}`, `HEAD~3`) works. fn range_ids(r: &Repo, range: &str, path: Option<&str>) -> IdeResult> { @@ -234,10 +331,11 @@ struct PathWalk<'r> { seen: HashSet, memo: HashMap>, seq: u64, + first_parent: bool, } impl<'r> PathWalk<'r> { - fn new(repo: &'r gix::Repository, tips: Vec, rel: String) -> IdeResult { + fn new(repo: &'r gix::Repository, tips: Vec, rel: String, first_parent: bool) -> IdeResult { let mut walk = PathWalk { repo, rel, @@ -245,6 +343,7 @@ impl<'r> PathWalk<'r> { seen: Default::default(), memo: Default::default(), seq: 0, + first_parent, }; for tip in tips { walk.push(tip)?; @@ -268,7 +367,12 @@ impl<'r> PathWalk<'r> { let commit = self.repo.find_commit(id).map_err(gx)?; let parents: Vec = commit.parent_ids().map(|p| p.detach()).collect(); // Parents cut off by a shallow boundary are treated as absent. - let present: Vec = parents.iter().copied().filter(|p| self.repo.has_object(p)).collect(); + let present: Vec = parents + .iter() + .copied() + .filter(|p| self.repo.has_object(p)) + .take(if self.first_parent { 1 } else { usize::MAX }) + .collect(); let mine = path_entry(self.repo, id, &self.rel, &mut self.memo)?; let mut same_parent = None; for p in &present { diff --git a/crates/ide-vcs/src/lib.rs b/crates/ide-vcs/src/lib.rs index 6194db2a..44e2dae9 100644 --- a/crates/ide-vcs/src/lib.rs +++ b/crates/ide-vcs/src/lib.rs @@ -88,6 +88,20 @@ pub struct Commit { pub summary: String, pub message: String, pub refs: Vec, + /// Committer time (Log › View Options › Commit Timestamp). + #[serde(default)] + pub commit_time_ms: f64, +} + +/// Order of the Git Log (Graph Options › Sort). +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub enum LogOrder { + /// `--topo-order`: lines of history are not intermixed. + #[default] + Topo, + /// `--date-order`: commit time, but never a parent before its children. + Date, } #[derive(Clone, Debug, Default, Serialize, Deserialize, Type)] @@ -115,6 +129,21 @@ pub struct LogQuery { /// More author needles next to `author`; a commit matches any of them (Log User filter). #[serde(default)] pub authors: Vec, + #[serde(default)] + pub order: LogOrder, + /// Follow only the first parent of merges (`--first-parent`). + #[serde(default)] + pub first_parent: bool, + /// Leave out merge commits (`--no-merges`); their children link to their parents. + #[serde(default)] + pub no_merges: bool, + /// Committer time bounds (epoch ms; `since` inclusive, `until` exclusive). Parents + /// outside the range are rewritten to their nearest shown ancestors (or dropped when + /// older), so the graph stays connected. + #[serde(default)] + pub since_ms: Option, + #[serde(default)] + pub until_ms: Option, } #[derive(Clone, Debug, Serialize, Deserialize, Type)] @@ -397,6 +426,7 @@ pub mod changelists; mod cli; mod diff; mod history; +mod manage; mod ops; mod patch; mod refs; @@ -409,6 +439,7 @@ mod write; pub use cli::{detected_git_executable, git_version, set_git_executable}; pub use diff::build_partial_patch; +pub use manage::{MergeOptions, RebaseOptions, RemoteEdit, Worktree}; pub use remotes::Remote; pub use roots::Repos; @@ -420,6 +451,11 @@ pub fn clone(url: &str, dest: &Path, cancel: &CancellationToken, on_output: &dyn cli::run_streaming(parent, &["clone", "--progress", "--", url, &dest], cancel, on_output) } +/// Creates an empty repository in `dir` (`git init`). +pub fn init(dir: &Path) -> IdeResult<()> { + cli::run(dir, &["init", "-q"], None).map(|_| ()) +} + /// Line hunks of `new_text` against `old_text` (CRLF-normalised, Histogram diff), in the /// order [`build_partial_patch`] indexes them. pub fn diff_hunks(old_text: &str, new_text: &str) -> Vec { diff --git a/crates/ide-vcs/src/manage.rs b/crates/ide-vcs/src/manage.rs new file mode 100644 index 00000000..2276e715 --- /dev/null +++ b/crates/ide-vcs/src/manage.rs @@ -0,0 +1,343 @@ +//! Git menu dialogs: Merge / Rebase / Pull with options, Manage Remotes and Worktrees. + +use crate::Repo; +use crate::cli::{self, run}; +use ide_core::{CancellationToken, IdeError, IdeResult}; +use serde::{Deserialize, Serialize}; +use specta::Type; +use std::path::Path; + +/// Merge dialog › Modify options (shared by the Pull dialog). +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct MergeOptions { + #[serde(default)] + pub no_ff: bool, + #[serde(default)] + pub ff_only: bool, + #[serde(default)] + pub squash: bool, + #[serde(default)] + pub no_commit: bool, + /// `-m`: message of the merge commit (Merge dialog only). + #[serde(default)] + pub message: Option, +} + +/// Rebase dialog options. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct RebaseOptions { + /// `--onto `: replay `upstream..HEAD` onto this revision instead. + #[serde(default)] + pub onto: Option, + #[serde(default)] + pub rebase_merges: bool, + #[serde(default)] + pub keep_empty: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct Worktree { + pub path: ide_core::IpcPath, + /// Checked-out commit; `None` for a bare main repository. + pub head: Option, + /// Short branch name; `None` when HEAD is detached. + pub branch: Option, + /// The main working tree (cannot be removed). + pub main: bool, + pub locked: bool, + pub prunable: bool, +} + +/// Manage Remotes edits. +#[derive(Clone, Debug, Serialize, Deserialize, Type)] +#[serde(tag = "op", rename_all = "camelCase")] +pub enum RemoteEdit { + Add { + name: String, + url: String, + }, + Remove { + name: String, + }, + /// Renames `name` to `new_name` (when different) and sets its URL. + Edit { + name: String, + new_name: String, + url: String, + }, +} + +fn arg(value: &str, what: &str) -> IdeResult { + let v = value.trim(); + if v.is_empty() || v.starts_with('-') { + return Err(IdeError::invalid(format!("invalid {what} '{value}'"))); + } + Ok(v.to_string()) +} + +fn merge_flags(o: &MergeOptions) -> IdeResult> { + if o.ff_only && (o.no_ff || o.squash) { + return Err(IdeError::invalid("--ff-only cannot be combined with --no-ff or --squash")); + } + if o.no_ff && o.squash { + return Err(IdeError::invalid("--no-ff cannot be combined with --squash")); + } + let mut flags = Vec::new(); + if o.no_ff { + flags.push("--no-ff"); + } + if o.ff_only { + flags.push("--ff-only"); + } + if o.squash { + flags.push("--squash"); + } + if o.no_commit { + flags.push("--no-commit"); + } + Ok(flags) +} + +/// `git merge` arguments for the Merge dialog (local changes are stashed and restored). +pub(crate) fn merge_args(branches: &[String], o: &MergeOptions) -> IdeResult> { + if branches.is_empty() { + return Err(IdeError::invalid("no branch to merge")); + } + let mut args: Vec = vec!["merge".into(), "--autostash".into()]; + args.extend(merge_flags(o)?.into_iter().map(String::from)); + match o.message.as_deref().map(str::trim).filter(|m| !m.is_empty()) { + Some(m) => args.extend(["-m".into(), m.to_string()]), + None => args.push("--no-edit".into()), + } + for b in branches { + args.push(arg(b, "branch")?); + } + Ok(args) +} + +/// `git rebase` arguments for the Rebase dialog. +pub(crate) fn rebase_args(upstream: &str, o: &RebaseOptions) -> IdeResult> { + let mut args: Vec = vec!["rebase".into(), "--autostash".into()]; + if o.rebase_merges { + args.push("--rebase-merges".into()); + } + if o.keep_empty { + args.push("--keep-empty".into()); + } + if let Some(onto) = o.onto.as_deref().map(str::trim).filter(|s| !s.is_empty()) { + args.extend(["--onto".into(), arg(onto, "revision")?]); + } + args.push(arg(upstream, "revision")?); + Ok(args) +} + +/// `git pull` arguments for the Pull dialog: `remote` and its `branches` (none = the +/// upstream's configured merge branch). +pub(crate) fn pull_args(remote: &str, branches: &[String], rebase: bool, o: &MergeOptions) -> IdeResult> { + let mut args: Vec = vec!["pull".into(), "--progress".into(), "--autostash".into()]; + if rebase { + if o.no_ff || o.ff_only || o.squash || o.no_commit { + return Err(IdeError::invalid("--rebase cannot be combined with merge options")); + } + args.push("--rebase".into()); + } else { + args.push("--no-rebase".into()); + args.extend(merge_flags(o)?.into_iter().map(String::from)); + } + args.push(arg(remote, "remote")?); + for b in branches { + args.push(arg(b, "branch")?); + } + Ok(args) +} + +/// Parses `git worktree list --porcelain`. +pub(crate) fn parse_worktrees(text: &str) -> Vec { + let mut out: Vec = Vec::new(); + for block in text.split("\n\n") { + let mut wt: Option = None; + let mut bare = false; + for line in block.lines() { + let (key, value) = line.split_once(' ').unwrap_or((line, "")); + match key { + "worktree" => { + wt = Some(Worktree { + path: value.into(), + head: None, + branch: None, + main: out.is_empty(), + locked: false, + prunable: false, + }) + } + "HEAD" => { + if let Some(w) = wt.as_mut() { + w.head = Some(value.to_string()); + } + } + "branch" => { + if let Some(w) = wt.as_mut() { + w.branch = Some(value.strip_prefix("refs/heads/").unwrap_or(value).to_string()); + } + } + "locked" => { + if let Some(w) = wt.as_mut() { + w.locked = true; + } + } + "prunable" => { + if let Some(w) = wt.as_mut() { + w.prunable = true; + } + } + "bare" => bare = true, + _ => {} + } + } + if let Some(mut w) = wt { + if bare { + w.head = None; + } + out.push(w); + } + } + out +} + +impl Repo { + /// Merges `branches` into the current branch (Git › Merge…). + pub fn merge_with(&self, branches: &[String], options: &MergeOptions) -> IdeResult<()> { + let args = merge_args(branches, options)?; + let args: Vec<&str> = args.iter().map(String::as_str).collect(); + run(self.root(), &args, None).map(|_| ()) + } + + /// Rebases the current branch onto `upstream` (or `upstream..HEAD` onto `options.onto`). + pub fn rebase_with(&self, upstream: &str, options: &RebaseOptions) -> IdeResult<()> { + let args = rebase_args(upstream, options)?; + let args: Vec<&str> = args.iter().map(String::as_str).collect(); + run(self.root(), &args, None).map(|_| ()) + } + + /// Git › Pull…: pulls `branches` of `remote` into the current branch. + pub fn pull_from( + &self, + remote: &str, + branches: &[String], + rebase: bool, + options: &MergeOptions, + cancel: &CancellationToken, + on_output: &dyn Fn(String), + ) -> IdeResult<()> { + let args = pull_args(remote, branches, rebase, options)?; + let args: Vec<&str> = args.iter().map(String::as_str).collect(); + cli::run_streaming(self.root(), &args, cancel, on_output) + } + + /// Manage Remotes: adds, removes or edits a remote. + pub fn edit_remote(&self, edit: &RemoteEdit) -> IdeResult<()> { + match edit { + RemoteEdit::Add { name, url } => { + let (name, url) = (arg(name, "remote name")?, arg(url, "URL")?); + run(self.root(), &["remote", "add", "--", &name, &url], None)?; + } + RemoteEdit::Remove { name } => { + let name = arg(name, "remote name")?; + run(self.root(), &["remote", "remove", &name], None)?; + } + RemoteEdit::Edit { name, new_name, url } => { + let (name, new_name, url) = + (arg(name, "remote name")?, arg(new_name, "remote name")?, arg(url, "URL")?); + if name != new_name { + run(self.root(), &["remote", "rename", &name, &new_name], None)?; + } + run(self.root(), &["remote", "set-url", "--", &new_name, &url], None)?; + } + } + Ok(()) + } + + /// Working trees of the repository, the main one first. + pub fn worktrees(&self) -> IdeResult> { + let out = run(self.root(), &["worktree", "list", "--porcelain"], None)?; + Ok(parse_worktrees(&out.stdout_str())) + } + + /// Git › New Worktree…: checks out `commitish` at `path`, on a new branch `new_branch` + /// when given (otherwise `commitish` must be a branch not checked out elsewhere). + pub fn add_worktree(&self, path: &Path, commitish: &str, new_branch: Option<&str>) -> IdeResult<()> { + let path = path.to_string_lossy().into_owned(); + let commitish = arg(commitish, "revision")?; + let mut args: Vec = vec!["worktree".into(), "add".into()]; + if let Some(b) = new_branch.map(str::trim).filter(|b| !b.is_empty()) { + args.extend(["-b".into(), arg(b, "branch name")?]); + } + args.extend(["--".into(), path, commitish]); + let args: Vec<&str> = args.iter().map(String::as_str).collect(); + run(self.root(), &args, None).map(|_| ()) + } + + pub fn remove_worktree(&self, path: &Path, force: bool) -> IdeResult<()> { + let path = path.to_string_lossy().into_owned(); + let mut args = vec!["worktree", "remove"]; + if force { + args.push("--force"); + } + args.extend(["--", &path]); + run(self.root(), &args, None).map(|_| ()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn merge_args_follow_the_options() { + let o = MergeOptions { no_ff: true, message: Some("Merge it".into()), ..Default::default() }; + assert_eq!( + merge_args(&["dev".into()], &o).unwrap(), + ["merge", "--autostash", "--no-ff", "-m", "Merge it", "dev"] + ); + assert_eq!( + merge_args(&["a".into(), "b".into()], &MergeOptions::default()).unwrap(), + ["merge", "--autostash", "--no-edit", "a", "b"] + ); + let bad = MergeOptions { ff_only: true, squash: true, ..Default::default() }; + assert!(merge_args(&["dev".into()], &bad).is_err()); + assert!(merge_args(&["--evil".into()], &MergeOptions::default()).is_err()); + assert!(merge_args(&[], &MergeOptions::default()).is_err()); + } + + #[test] + fn rebase_and_pull_args() { + let o = RebaseOptions { onto: Some("main".into()), rebase_merges: true, keep_empty: false }; + assert_eq!( + rebase_args("feature~3", &o).unwrap(), + ["rebase", "--autostash", "--rebase-merges", "--onto", "main", "feature~3"] + ); + assert_eq!( + pull_args("origin", &["main".into()], false, &MergeOptions { ff_only: true, ..Default::default() }) + .unwrap(), + ["pull", "--progress", "--autostash", "--no-rebase", "--ff-only", "origin", "main"] + ); + assert_eq!( + pull_args("origin", &[], true, &MergeOptions::default()).unwrap(), + ["pull", "--progress", "--autostash", "--rebase", "origin"] + ); + assert!(pull_args("origin", &[], true, &MergeOptions { squash: true, ..Default::default() }).is_err()); + } + + #[test] + fn parses_worktree_list() { + let text = "worktree /repo\nHEAD 1111\nbranch refs/heads/main\n\nworktree /wt/fix\nHEAD 2222\ndetached\nlocked\n\nworktree /wt/gone\nHEAD 3333\nbranch refs/heads/old\nprunable gitdir file points to non-existent location\n"; + let wts = parse_worktrees(text); + assert_eq!(wts.len(), 3); + assert!(wts[0].main && wts[0].branch.as_deref() == Some("main")); + assert!(!wts[1].main && wts[1].branch.is_none() && wts[1].locked); + assert!(wts[2].prunable && wts[2].head.as_deref() == Some("3333")); + } +} diff --git a/crates/ide-vcs/tests/log_shape.rs b/crates/ide-vcs/tests/log_shape.rs new file mode 100644 index 00000000..a6802f35 --- /dev/null +++ b/crates/ide-vcs/tests/log_shape.rs @@ -0,0 +1,175 @@ +//! Git Log graph options (order, first parent, no merges, date range) and the Git menu +//! dialogs' operations (merge / rebase with options, remotes, worktrees). + +use ide_vcs::*; +use std::path::Path; +use std::process::Command; + +fn git(dir: &Path, args: &[&str], time: Option) -> String { + let mut cmd = Command::new("git"); + cmd.current_dir(dir) + .args(["-c", "init.defaultBranch=main", "-c", "user.name=Test", "-c", "user.email=t@example.com"]) + .args(["-c", "commit.gpgsign=false", "-c", "core.hooksPath=.git/hooks"]) + .args(args) + .env("GIT_TERMINAL_PROMPT", "0"); + if let Some(t) = time { + let date = format!("@{t} +0000"); + cmd.env("GIT_AUTHOR_DATE", &date).env("GIT_COMMITTER_DATE", &date); + } + let out = cmd.output().expect("git runs"); + assert!(out.status.success(), "git {args:?} failed: {}", String::from_utf8_lossy(&out.stderr)); + String::from_utf8_lossy(&out.stdout).trim().to_string() +} + +fn commit(dir: &Path, file: &str, msg: &str, time: i64) -> String { + std::fs::write(dir.join(file), msg).unwrap(); + git(dir, &["add", "-A"], None); + git(dir, &["commit", "-q", "-m", msg], Some(time)); + git(dir, &["rev-parse", "HEAD"], None) +} + +struct History { + _tmp: tempfile::TempDir, + repo: Repo, + c1: String, + s1: String, + c2: String, + m: String, + c3: String, +} + +/// c1 ← s1 (side) ; c1 ← c2 ← m (merges s1) ← c3, committed at 1000…5000 s. +fn history() -> History { + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path().to_path_buf(); + git(&dir, &["init", "-q"], None); + let c1 = commit(&dir, "a.txt", "c1", 1000); + git(&dir, &["checkout", "-q", "-b", "side"], None); + let s1 = commit(&dir, "s.txt", "s1", 2000); + git(&dir, &["checkout", "-q", "main"], None); + let c2 = commit(&dir, "b.txt", "c2", 3000); + git(&dir, &["merge", "-q", "--no-ff", "-m", "merge side", "side"], Some(4000)); + let m = git(&dir, &["rev-parse", "HEAD"], None); + let c3 = commit(&dir, "c.txt", "c3", 5000); + let repo = Repo::discover(&dir).unwrap().expect("repository"); + History { _tmp: tmp, repo, c1, s1, c2, m, c3 } +} + +fn log(h: &History, q: LogQuery) -> Vec { + h.repo.log(&LogQuery { branches: vec!["main".into()], ..q }).unwrap() +} + +fn ids(commits: &[Commit]) -> Vec { + commits.iter().map(|c| c.id.clone()).collect() +} + +fn parents_of(commits: &[Commit], id: &str) -> Vec { + commits.iter().find(|c| c.id == id).unwrap().parents.clone() +} + +#[test] +fn date_order_follows_commit_time_and_reports_it() { + let h = history(); + let out = log(&h, LogQuery { order: LogOrder::Date, ..Default::default() }); + assert_eq!(ids(&out), vec![h.c3.clone(), h.m.clone(), h.c2.clone(), h.s1.clone(), h.c1.clone()]); + assert_eq!(out[0].commit_time_ms, 5_000_000.0); +} + +#[test] +fn no_merges_links_children_to_the_merge_parents() { + let h = history(); + let out = log(&h, LogQuery { no_merges: true, ..Default::default() }); + assert!(!ids(&out).contains(&h.m)); + assert_eq!(out.len(), 4); + assert_eq!(parents_of(&out, &h.c3), vec![h.c2.clone(), h.s1.clone()]); +} + +#[test] +fn first_parent_skips_merged_branches() { + let h = history(); + let out = log(&h, LogQuery { first_parent: true, ..Default::default() }); + assert_eq!(ids(&out), vec![h.c3.clone(), h.m.clone(), h.c2.clone(), h.c1.clone()]); + assert_eq!(parents_of(&out, &h.m), vec![h.c2.clone()]); +} + +#[test] +fn date_range_keeps_the_graph_connected() { + let h = history(); + let since = log(&h, LogQuery { since_ms: Some(2_500_000.0), ..Default::default() }); + assert_eq!(ids(&since), vec![h.c3.clone(), h.m.clone(), h.c2.clone()]); + // Older parents are not shown, so they are dropped. + assert_eq!(parents_of(&since, &h.m), vec![h.c2.clone()]); + assert!(parents_of(&since, &h.c2).is_empty()); + + let window = log(&h, LogQuery { since_ms: Some(1_500_000.0), until_ms: Some(4_500_000.0), ..Default::default() }); + let mut got = ids(&window); + got.sort(); + let mut want = vec![h.m.clone(), h.c2.clone(), h.s1.clone()]; + want.sort(); + assert_eq!(got, want); + + // A commit newer than `until` is skipped and its child links past it. + let until = log(&h, LogQuery { until_ms: Some(3_500_000.0), order: LogOrder::Date, ..Default::default() }); + assert_eq!(ids(&until), vec![h.c2.clone(), h.s1.clone(), h.c1.clone()]); + let combined = log(&h, LogQuery { until_ms: Some(4_500_000.0), no_merges: true, ..Default::default() }); + assert!(!ids(&combined).contains(&h.m) && !ids(&combined).contains(&h.c3)); +} + +#[test] +fn merge_with_options_and_rebase_onto() { + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path(); + git(dir, &["init", "-q"], None); + let base = commit(dir, "a.txt", "base", 1000); + git(dir, &["checkout", "-q", "-b", "feature"], None); + let f1 = commit(dir, "f.txt", "f1", 2000); + git(dir, &["checkout", "-q", "main"], None); + let repo = Repo::discover(dir).unwrap().unwrap(); + + let ff_only = MergeOptions { ff_only: true, squash: true, ..Default::default() }; + assert!(repo.merge_with(&["feature".into()], &ff_only).is_err()); + let no_ff = MergeOptions { no_ff: true, message: Some("Merge feature now".into()), ..Default::default() }; + repo.merge_with(&["feature".into()], &no_ff).unwrap(); + assert_eq!(git(dir, &["log", "-1", "--format=%P %s"], None), format!("{base} {f1} Merge feature now")); + + git(dir, &["checkout", "-q", "-b", "topic", &base], None); + commit(dir, "t.txt", "topic", 3000); + repo.rebase_with("main", &RebaseOptions::default()).unwrap(); + assert_eq!(git(dir, &["rev-list", "--count", "main..topic"], None), "1"); + assert_eq!(git(dir, &["merge-base", "main", "topic"], None), git(dir, &["rev-parse", "main"], None)); +} + +#[test] +fn remotes_and_worktrees_are_managed() { + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path().join("work"); + std::fs::create_dir(&dir).unwrap(); + git(&dir, &["init", "-q"], None); + commit(&dir, "a.txt", "base", 1000); + let repo = Repo::discover(&dir).unwrap().unwrap(); + + repo.edit_remote(&RemoteEdit::Add { name: "origin".into(), url: "https://example.com/a.git".into() }).unwrap(); + repo.edit_remote(&RemoteEdit::Edit { + name: "origin".into(), + new_name: "upstream".into(), + url: "https://example.com/b.git".into(), + }) + .unwrap(); + assert_eq!( + repo.remotes().unwrap(), + vec![Remote { name: "upstream".into(), url: "https://example.com/b.git".into() }] + ); + assert!(repo.edit_remote(&RemoteEdit::Add { name: "--bad".into(), url: "x".into() }).is_err()); + repo.edit_remote(&RemoteEdit::Remove { name: "upstream".into() }).unwrap(); + assert!(repo.remotes().unwrap().is_empty()); + + let wt = tmp.path().join("work-fix"); + repo.add_worktree(&wt, "main", Some("fix")).unwrap(); + let list = repo.worktrees().unwrap(); + assert_eq!(list.len(), 2); + assert!(list[0].main && list[0].branch.as_deref() == Some("main")); + assert_eq!(list[1].branch.as_deref(), Some("fix")); + assert!(wt.join("a.txt").exists()); + repo.remove_worktree(&wt, false).unwrap(); + assert_eq!(repo.worktrees().unwrap().len(), 1); +} diff --git a/crates/ide-vfs/Cargo.toml b/crates/ide-vfs/Cargo.toml index b635d508..bec96e84 100644 --- a/crates/ide-vfs/Cargo.toml +++ b/crates/ide-vfs/Cargo.toml @@ -18,6 +18,7 @@ chardetng.workspace = true trash.workspace = true tracing.workspace = true parking_lot.workspace = true +zip = { version = "=2.4.2", default-features = false, features = ["deflate"] } [dev-dependencies] tempfile.workspace = true diff --git a/crates/ide-vfs/src/archive.rs b/crates/ide-vfs/src/archive.rs new file mode 100644 index 00000000..f6915539 --- /dev/null +++ b/crates/ide-vfs/src/archive.rs @@ -0,0 +1,337 @@ +//! Read-only browsing of zip archives (jar, zip, war…), IDEA's `jar://` file +//! system: `!` is the archive's root and `!/` an entry +//! inside it. Listing and reading go through a small cache of parsed central +//! directories, refreshed when the archive changes on disk. + +use crate::{EntryKind, FileEntry, mtime_ms, natural_cmp}; +use ide_core::{IdeError, IdeResult}; +use parking_lot::Mutex; +use std::collections::HashMap; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, LazyLock}; +use std::time::SystemTime; + +/// File extensions browsed as zip archives. +pub const ARCHIVE_EXTENSIONS: &[&str] = &["jar", "zip", "war", "ear", "aar", "srcjar"]; + +/// Separator between the archive and the entry path (`/jdk/lib/src.zip!/java.base/…`). +pub const ARCHIVE_SEPARATOR: char = '!'; + +/// Parsed archives kept open. +const CACHE_SIZE: usize = 8; + +/// Whether `path` names a file browsed as an archive (by extension). +pub fn is_archive_file(path: &Path) -> bool { + path.extension() + .and_then(|e| e.to_str()) + .is_some_and(|e| ARCHIVE_EXTENSIONS.iter().any(|a| a.eq_ignore_ascii_case(e))) +} + +/// `(archive, entry)` for `!` (entry `""`) or `!/`; +/// `None` for ordinary paths. The entry is `/`-separated without leading or trailing `/`. +pub fn split_archive_path(path: &Path) -> Option<(PathBuf, String)> { + let s = path.to_str()?; + for (i, _) in s.match_indices(ARCHIVE_SEPARATOR) { + let rest = &s[i + 1..]; + if !(rest.is_empty() || rest.starts_with(['/', '\\'])) { + continue; + } + let archive = Path::new(&s[..i]); + if is_archive_file(archive) { + let entry = rest.replace('\\', "/"); + return Some((archive.to_path_buf(), entry.trim_matches('/').to_owned())); + } + } + None +} + +/// Whether `path` lies inside an archive (`!…`). +pub fn is_archive_path(path: &Path) -> bool { + split_archive_path(path).is_some() +} + +/// The root of `archive` as a browsable folder: `!`. +pub fn archive_root(archive: &Path) -> PathBuf { + let mut s = archive.as_os_str().to_owned(); + s.push("!"); + PathBuf::from(s) +} + +/// `!/` (the root for an empty entry). +pub fn archive_entry_path(archive: &Path, entry: &str) -> PathBuf { + let entry = entry.trim_matches('/'); + if entry.is_empty() { + return archive_root(archive); + } + let mut s = archive.as_os_str().to_owned(); + s.push("!/"); + s.push(entry); + PathBuf::from(s) +} + +struct Node { + dir: bool, + size: u64, + /// Index in the zip's central directory (`None` for implicit directories). + index: Option, + children: Vec, +} + +struct Archive { + stamp: (Option, u64), + mtime_ms: f64, + /// Entry path (`""` = root) → node. + nodes: HashMap, + zip: Mutex>, +} + +/// Most recently used first. +type Cache = Vec<(PathBuf, Arc)>; + +static CACHE: LazyLock> = LazyLock::new(Default::default); + +fn zip_error(archive: &Path, e: zip::result::ZipError) -> IdeError { + match e { + zip::result::ZipError::Io(e) => e.into(), + zip::result::ZipError::FileNotFound => IdeError::not_found(format!("entry not found in {}", archive.display())), + e => IdeError::invalid(format!("{}: {e}", archive.display())), + } +} + +/// Normalises a zip entry name: `/`-separated, no leading `/` or `./`, no trailing `/`. +fn clean_name(name: &str) -> Option { + let parts: Vec<&str> = name.split(['/', '\\']).filter(|p| !p.is_empty() && *p != ".").collect(); + (!parts.is_empty() && !parts.contains(&"..")).then(|| parts.join("/")) +} + +fn parent_of(name: &str) -> (&str, &str) { + match name.rfind('/') { + Some(i) => (&name[..i], &name[i + 1..]), + None => ("", name), + } +} + +impl Archive { + fn open(path: &Path, stamp: (Option, u64), mtime_ms: f64) -> IdeResult { + let mut zip = zip::ZipArchive::new(File::open(path)?).map_err(|e| zip_error(path, e))?; + let mut nodes: HashMap = HashMap::new(); + nodes.insert(String::new(), Node { dir: true, size: 0, index: None, children: Vec::new() }); + for i in 0..zip.len() { + let Ok(file) = zip.by_index_raw(i) else { continue }; + let Some(name) = clean_name(file.name()) else { continue }; + let (dir, size) = (file.is_dir(), file.size()); + drop(file); + let mut parent = parent_of(&name).0; + while !nodes.contains_key(parent) { + nodes.insert(parent.to_owned(), Node { dir: true, size: 0, index: None, children: Vec::new() }); + parent = parent_of(parent).0; + } + let node = nodes.entry(name).or_insert(Node { dir, size: 0, index: None, children: Vec::new() }); + node.dir |= dir; + node.index = Some(i); + node.size = if node.dir { 0 } else { size }; + } + let links: Vec<(String, String)> = nodes + .keys() + .filter(|k| !k.is_empty()) + .map(|k| { + let (parent, base) = parent_of(k); + (parent.to_owned(), base.to_owned()) + }) + .collect(); + for (parent, base) in links { + if let Some(p) = nodes.get_mut(&parent) { + p.dir = true; + p.children.push(base); + } + } + Ok(Self { stamp, mtime_ms, nodes, zip: Mutex::new(zip) }) + } +} + +/// The parsed archive at `path`, from the cache unless it changed on disk. +fn archive(path: &Path) -> IdeResult> { + let meta = std::fs::metadata(path)?; + if !meta.is_file() { + return Err(IdeError::invalid(format!("{} is not an archive", path.display()))); + } + let stamp = (meta.modified().ok(), meta.len()); + { + let mut cache = CACHE.lock(); + if let Some(i) = cache.iter().position(|(p, a)| p == path && a.stamp == stamp) { + let hit = cache.remove(i); + let a = hit.1.clone(); + cache.insert(0, hit); + return Ok(a); + } + } + let parsed = Arc::new(Archive::open(path, stamp, mtime_ms(&meta))?); + let mut cache = CACHE.lock(); + cache.retain(|(p, _)| p != path); + cache.insert(0, (path.to_path_buf(), parsed.clone())); + cache.truncate(CACHE_SIZE); + Ok(parsed) +} + +/// Lists one folder of an archive (`dir` = `""` for its root): directories +/// first, then files, in natural order. Entries are never ignored or excluded. +pub fn read_archive_dir(archive_path: &Path, dir: &str) -> IdeResult> { + let a = archive(archive_path)?; + let dir = dir.trim_matches('/'); + let node = a + .nodes + .get(dir) + .ok_or_else(|| IdeError::not_found(format!("{dir} not found in {}", archive_path.display())))?; + if !node.dir { + return Err(IdeError::invalid(format!("{dir} is not a directory"))); + } + let mut entries: Vec = node + .children + .iter() + .filter_map(|name| { + let full = if dir.is_empty() { name.clone() } else { format!("{dir}/{name}") }; + let child = a.nodes.get(&full)?; + Some(FileEntry { + name: name.clone(), + path: archive_entry_path(archive_path, &full).into(), + kind: if child.dir { EntryKind::Dir } else { EntryKind::File }, + size: child.size, + mtime_ms: a.mtime_ms, + ignored: false, + excluded: false, + hidden: name.starts_with('.'), + target_kind: None, + }) + }) + .collect(); + entries.sort_by(|a, b| { + let (da, db) = (a.kind == EntryKind::Dir, b.kind == EntryKind::Dir); + db.cmp(&da).then_with(|| natural_cmp(&a.name, &b.name)) + }); + Ok(entries) +} + +/// Uncompressed size of an archive entry. +pub(crate) fn entry_size(archive_path: &Path, entry: &str) -> IdeResult<(u64, f64)> { + let a = archive(archive_path)?; + let node = a + .nodes + .get(entry) + .ok_or_else(|| IdeError::not_found(format!("{entry} not found in {}", archive_path.display())))?; + if node.dir { + return Err(IdeError::invalid(format!("{entry} is a directory"))); + } + Ok((node.size, a.mtime_ms)) +} + +/// The contents of a file inside an archive, and the archive's mtime (ms). +pub fn read_archive_entry(archive_path: &Path, entry: &str) -> IdeResult<(Vec, f64)> { + let a = archive(archive_path)?; + let index = match a.nodes.get(entry) { + Some(Node { dir: false, index: Some(i), .. }) => *i, + Some(_) => return Err(IdeError::invalid(format!("{entry} is a directory"))), + None => return Err(IdeError::not_found(format!("{entry} not found in {}", archive_path.display()))), + }; + let mut zip = a.zip.lock(); + let mut file = zip.by_index(index).map_err(|e| zip_error(archive_path, e))?; + let mut bytes = Vec::with_capacity(file.size().min(crate::MAX_OPEN_SIZE) as usize); + file.read_to_end(&mut bytes)?; + Ok((bytes, a.mtime_ms)) +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use std::io::Write; + + /// Writes a zip with `files` (`name` ending in `/` = explicit directory entry). + pub(crate) fn write_zip(path: &Path, files: &[(&str, &str)]) { + let mut w = zip::ZipWriter::new(File::create(path).unwrap()); + let opts = zip::write::SimpleFileOptions::default(); + for (name, text) in files { + if name.ends_with('/') { + w.add_directory(*name, opts).unwrap(); + } else { + w.start_file(*name, opts).unwrap(); + w.write_all(text.as_bytes()).unwrap(); + } + } + w.finish().unwrap(); + } + + #[test] + fn splits_archive_paths() { + assert_eq!(split_archive_path(Path::new("/a/b.txt")), None); + assert_eq!(split_archive_path(Path::new("/a/x!/y")), None, "not an archive extension"); + assert_eq!(split_archive_path(Path::new("/a/s.zip!")), Some((PathBuf::from("/a/s.zip"), String::new()))); + assert_eq!( + split_archive_path(Path::new("/a/lib.JAR!/org/x/A.java")), + Some((PathBuf::from("/a/lib.JAR"), "org/x/A.java".into())) + ); + assert_eq!(split_archive_path(Path::new("/a/s.zip!/dir/")), Some((PathBuf::from("/a/s.zip"), "dir".into()))); + assert_eq!(archive_entry_path(Path::new("/a/s.zip"), "x/y.java"), PathBuf::from("/a/s.zip!/x/y.java")); + assert_eq!(archive_entry_path(Path::new("/a/s.zip"), ""), PathBuf::from("/a/s.zip!")); + assert!(is_archive_path(Path::new("/a/s.zip!/x"))); + } + + #[test] + fn lists_and_reads_entries_with_implicit_directories() { + let tmp = tempfile::tempdir().unwrap(); + let zip = tmp.path().join("src.zip"); + write_zip( + &zip, + &[ + ("java.base/java/lang/String.java", "class String {}"), + ("java.base/java/lang/Object.java", "class Object {}"), + ("java.base/java/util/", ""), + ("java.base/module-info.java", "module java.base {}"), + ("README", "hi"), + ("../evil", "x"), + ], + ); + let names = |dir: &str| -> Vec<(String, EntryKind)> { + read_archive_dir(&zip, dir).unwrap().into_iter().map(|e| (e.name, e.kind)).collect() + }; + assert_eq!(names(""), vec![("java.base".into(), EntryKind::Dir), ("README".into(), EntryKind::File)]); + assert_eq!( + names("java.base"), + vec![("java".into(), EntryKind::Dir), ("module-info.java".into(), EntryKind::File)] + ); + assert_eq!(names("java.base/java"), vec![("lang".into(), EntryKind::Dir), ("util".into(), EntryKind::Dir)]); + let lang = read_archive_dir(&zip, "java.base/java/lang").unwrap(); + assert_eq!(lang.iter().map(|e| e.name.as_str()).collect::>(), ["Object.java", "String.java"]); + assert_eq!(*lang[1].path, *archive_entry_path(&zip, "java.base/java/lang/String.java")); + assert!(names("java.base/java/util").is_empty()); + assert_eq!(read_archive_entry(&zip, "java.base/java/lang/String.java").unwrap().0, b"class String {}"); + assert_eq!(read_archive_entry(&zip, "missing").unwrap_err().kind, ide_core::error::ErrorKind::NotFound); + assert!(read_archive_entry(&zip, "java.base").is_err()); + assert!(read_archive_dir(&zip, "README").is_err()); + + // A rewritten archive is re-read. + write_zip(&zip, &[("only.txt", "1")]); + let f = File::options().write(true).open(&zip).unwrap(); + f.set_modified(SystemTime::now() + std::time::Duration::from_secs(5)).unwrap(); + assert_eq!(names(""), vec![("only.txt".into(), EntryKind::File)]); + } + + #[test] + fn read_dir_and_load_text_go_through_archives() { + let tmp = tempfile::tempdir().unwrap(); + let jar = tmp.path().join("lib-sources.jar"); + write_zip(&jar, &[("org/x/A.java", "class A {}\n"), ("org/x/B.class", "\0\0bin")]); + let root = crate::read_dir(&archive_root(&jar), &[]).unwrap(); + assert_eq!(root.iter().map(|e| e.name.as_str()).collect::>(), ["org"]); + assert_eq!(crate::read_dir(&jar, &[]).unwrap().len(), 1, "the archive file itself lists its root"); + let a = crate::load_text(&archive_entry_path(&jar, "org/x/A.java")).unwrap(); + assert_eq!((a.text.as_str(), a.encoding.as_str(), a.binary), ("class A {}\n", "utf-8", false)); + assert!(crate::load_text(&archive_entry_path(&jar, "org/x/B.class")).unwrap().binary); + let gbk = crate::load_text_as(&archive_entry_path(&jar, "org/x/A.java"), "gbk").unwrap(); + assert_eq!(gbk.encoding, "gbk"); + let probe = crate::probe(&archive_entry_path(&jar, "org/x/B.class")).unwrap(); + assert!(probe.binary && probe.size == 5); + let range = crate::read_range(&archive_entry_path(&jar, "org/x/A.java"), 6, 3).unwrap(); + assert_eq!((range.offset, range.data.as_str(), range.total), (6, crate::base64(b"A {").as_str(), 11)); + } +} diff --git a/crates/ide-vfs/src/bytes.rs b/crates/ide-vfs/src/bytes.rs index 00e4f60d..128b1cef 100644 --- a/crates/ide-vfs/src/bytes.rs +++ b/crates/ide-vfs/src/bytes.rs @@ -36,8 +36,24 @@ pub struct ByteRange { pub total: u64, } +/// Entry bytes of an archive path (`!/`), `None` for ordinary paths. +fn archive_bytes(path: &Path, limit: u64) -> IdeResult>> { + let Some((archive, entry)) = crate::split_archive_path(path) else { return Ok(None) }; + let (size, _) = crate::archive::entry_size(&archive, &entry)?; + if size > limit { + return Err(IdeError::invalid(format!("{} is too large ({} MiB)", path.display(), size / (1024 * 1024)))); + } + Ok(Some(crate::read_archive_entry(&archive, &entry)?.0)) +} + /// Reads only the head of the file to classify it. pub fn probe(path: &Path) -> IdeResult { + if let Some((archive, entry)) = crate::split_archive_path(path) { + let (size, _) = crate::archive::entry_size(&archive, &entry)?; + let head = + if size > crate::MAX_OPEN_SIZE { Vec::new() } else { crate::read_archive_entry(&archive, &entry)?.0 }; + return Ok(FileProbe { size, binary: looks_binary(&head), too_large: size > READ_ONLY_THRESHOLD }); + } let meta = fs::metadata(path)?; if meta.is_dir() { return Err(IdeError::invalid(format!("{} is a directory", path.display()))); @@ -49,6 +65,12 @@ pub fn probe(path: &Path) -> IdeResult { /// Reads up to `len` bytes (capped at [`MAX_RANGE_LEN`]) from `offset`. pub fn read_range(path: &Path, offset: u64, len: u32) -> IdeResult { + if let Some(bytes) = archive_bytes(path, crate::MAX_OPEN_SIZE)? { + let total = bytes.len() as u64; + let offset = offset.min(total); + let end = (offset + u64::from(len.min(MAX_RANGE_LEN))).min(total); + return Ok(ByteRange { offset, data: base64(&bytes[offset as usize..end as usize]), total }); + } let mut file = File::open(path)?; let total = file.metadata()?.len(); let offset = offset.min(total); @@ -79,6 +101,9 @@ pub fn image_mime(path: &Path) -> Option<&'static str> { pub fn read_data_url(path: &Path) -> IdeResult { let mime = image_mime(path).ok_or_else(|| IdeError::invalid(format!("{} is not a supported image", path.display())))?; + if let Some(bytes) = archive_bytes(path, MAX_IMAGE_SIZE)? { + return Ok(format!("data:{mime};base64,{}", base64(&bytes))); + } let meta = fs::metadata(path)?; if meta.len() > MAX_IMAGE_SIZE { return Err(IdeError::invalid(format!( diff --git a/crates/ide-vfs/src/encoding.rs b/crates/ide-vfs/src/encoding.rs index cc381db8..6a3d1d8d 100644 --- a/crates/ide-vfs/src/encoding.rs +++ b/crates/ide-vfs/src/encoding.rs @@ -94,7 +94,34 @@ pub(crate) fn decode(bytes: &[u8]) -> (String, &'static Encoding, bool, bool) { (text.into_owned(), enc, false, false) } +/// Bytes of a file inside an archive (size-checked) and the archive's mtime. +fn archive_bytes(path: &Path) -> IdeResult, f64)>> { + let Some((archive, entry)) = crate::split_archive_path(path) else { return Ok(None) }; + let (size, _) = crate::archive::entry_size(&archive, &entry)?; + if size > MAX_OPEN_SIZE { + return Err(IdeError::invalid(format!( + "{} is too large to open ({} MiB)", + path.display(), + size / (1024 * 1024) + ))); + } + crate::read_archive_entry(&archive, &entry).map(Some) +} + pub(crate) fn load_text(path: &Path) -> IdeResult { + if let Some((bytes, mtime_ms)) = archive_bytes(path)? { + let len = bytes.len() as u64; + let (text, enc, has_bom, binary) = decode_owned(bytes); + return Ok(LoadedText { + text, + encoding: encoding_label(enc), + has_bom, + binary, + large: len > LARGE_FILE_THRESHOLD, + too_large: len > READ_ONLY_THRESHOLD, + mtime_ms, + }); + } let meta = fs::metadata(path)?; if meta.is_dir() { return Err(IdeError::invalid(format!("{} is a directory", path.display()))); @@ -126,7 +153,10 @@ pub(crate) fn load_text_as(path: &Path, encoding: &str) -> IdeResult let enc = Encoding::for_label(encoding.trim().as_bytes()) .ok_or_else(|| IdeError::invalid(format!("unknown encoding {encoding:?}")))?; let mut loaded = load_text(path)?; - let bytes = fs::read(path)?; + let bytes = match archive_bytes(path)? { + Some((bytes, _)) => bytes, + None => fs::read(path)?, + }; let (body, has_bom) = match Encoding::for_bom(&bytes) { Some((bom_enc, len)) if bom_enc == enc => (&bytes[len..], true), _ => (&bytes[..], false), diff --git a/crates/ide-vfs/src/lib.rs b/crates/ide-vfs/src/lib.rs index ee528ac4..baa0c796 100644 --- a/crates/ide-vfs/src/lib.rs +++ b/crates/ide-vfs/src/lib.rs @@ -1,5 +1,6 @@ //! File system access for the IDE. The UI never touches the disk directly. +mod archive; mod bytes; mod encoding; mod ops; @@ -14,6 +15,10 @@ use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; +pub use archive::{ + ARCHIVE_EXTENSIONS, archive_entry_path, archive_root, is_archive_file, is_archive_path, read_archive_dir, + read_archive_entry, split_archive_path, +}; pub use bytes::{ByteRange, FileProbe, MAX_RANGE_LEN, base64, image_mime, probe, read_data_url, read_range}; use encoding::SNIFF_LEN; pub use encoding::{LARGE_FILE_THRESHOLD, MAX_OPEN_SIZE, READ_ONLY_THRESHOLD, encode, write_atomic}; @@ -100,7 +105,15 @@ pub fn natural_cmp(a: &str, b: &str) -> Ordering { /// Symlinks are reported as [`EntryKind::Symlink`] but sorted (and sized) by /// what they point to. `excluded` reflects `excludes` (relative to the enclosing /// repository root, else to `dir`); `ignored` also the applicable `.gitignore`/`.ignore` files. +/// +/// Inside an archive (`!/`, or the archive file itself) lists its entries. pub fn read_dir(dir: &Path, excludes: &[String]) -> IdeResult> { + if let Some((archive, entry)) = split_archive_path(dir) { + return read_archive_dir(&archive, &entry); + } + if is_archive_file(dir) && fs::metadata(dir).is_ok_and(|m| m.is_file()) { + return read_archive_dir(dir, ""); + } if !fs::metadata(dir)?.is_dir() { return Err(IdeError::invalid(format!("{} is not a directory", dir.display()))); } diff --git a/crates/ide-vfs/src/watch.rs b/crates/ide-vfs/src/watch.rs index 570aeb87..3cc884e4 100644 --- a/crates/ide-vfs/src/watch.rs +++ b/crates/ide-vfs/src/watch.rs @@ -12,7 +12,7 @@ use ide_core::{IdeError, IdeEvent, IdeResult, SharedSink}; use notify_debouncer_full::notify::event::{ModifyKind, RenameMode}; use notify_debouncer_full::notify::{self, EventKind, RecommendedWatcher, RecursiveMode, Watcher as _}; use notify_debouncer_full::{DebounceEventResult, DebouncedEvent, Debouncer, NoCache, new_debouncer_opt}; -use parking_lot::{Condvar, Mutex}; +use parking_lot::{Condvar, Mutex, RwLock}; use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::path::{Component, Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -47,8 +47,11 @@ type OnChange = Box; /// no callback runs after `drop` returns. pub struct Watcher { inner: Inner, + excludes: SharedExcludes, } +type SharedExcludes = Arc>; + enum Inner { Recursive(#[allow(dead_code)] Debouncer), PerDirectory { state: Arc, thread: Option> }, @@ -68,6 +71,7 @@ impl Watcher { ) -> IdeResult { let root = dunce::canonicalize(root)?; let filter = EventFilter::new(root.clone(), &excludes); + let shared = filter.excludes.clone(); let emit = move |batch: &FsBatch| { sink.emit(IdeEvent::FsChanged { created: batch.created.iter().map(Into::into).collect(), @@ -76,10 +80,18 @@ impl Watcher { }); on_change(batch); }; - match mode { + let inner = match mode { WatchMode::Recursive => Self::start_recursive(root, filter, emit), WatchMode::PerDirectory => Self::start_per_directory(root, filter, emit), - } + }?; + Ok(Self { inner, excludes: shared }) + } + + /// Replaces the exclude globs (Settings › Project Structure / File Types + /// changed): later events below newly excluded folders are dropped. In + /// per-directory mode, folders no longer excluded are watched once they change. + pub fn set_excludes(&self, excludes: &[String]) { + *self.excludes.write() = ExcludeMatcher::new(excludes); } /// Number of OS-level watches (1 in recursive mode). @@ -94,7 +106,7 @@ impl Watcher { root: PathBuf, filter: EventFilter, emit: impl Fn(&FsBatch) + Send + 'static, - ) -> IdeResult { + ) -> IdeResult { // No file-id cache: building it walks and stats the whole tree before `watch` returns, // which made opening large projects block for seconds. Renames arrive as remove + create. let handler = move |result: DebounceEventResult| match result { @@ -116,14 +128,14 @@ impl Watcher { debouncer .watch(&root, RecursiveMode::Recursive) .map_err(|e| io_error(format!("cannot watch {}: {e}", root.display())))?; - Ok(Self { inner: Inner::Recursive(debouncer) }) + Ok(Inner::Recursive(debouncer)) } fn start_per_directory( root: PathBuf, filter: EventFilter, emit: impl Fn(&FsBatch) + Send + 'static, - ) -> IdeResult { + ) -> IdeResult { let state = Arc::new(DirState { filter, watcher: Mutex::new(None), @@ -160,7 +172,7 @@ impl Watcher { .spawn(move || state.run(emit)) .map_err(|e| io_error(format!("cannot start file watcher: {e}")))? }; - Ok(Self { inner: Inner::PerDirectory { state, thread: Some(thread) } }) + Ok(Inner::PerDirectory { state, thread: Some(thread) }) } } @@ -359,13 +371,17 @@ fn merge(prev: Option, next: Change) -> Change { pub(crate) struct EventFilter { root: PathBuf, - excludes: ExcludeMatcher, + excludes: SharedExcludes, gitignore: GitignoreStack, } impl EventFilter { pub fn new(root: PathBuf, excludes: &[String]) -> Self { - Self { gitignore: GitignoreStack::new(&root), excludes: ExcludeMatcher::new(excludes), root } + Self { + gitignore: GitignoreStack::new(&root), + excludes: Arc::new(RwLock::new(ExcludeMatcher::new(excludes))), + root, + } } /// `Some(true)` for interesting VCS metadata, `Some(false)` for other `.git` @@ -405,13 +421,15 @@ impl EventFilter { _ => false, }; } - !self.excludes.is_excluded(rel) && !self.gitignore.is_ignored(dir, true) + !self.excludes.read().is_excluded(rel) && !self.gitignore.is_ignored(dir, true) } /// Whether a path found by scanning a new directory would be reported. fn is_reported(&self, path: &Path, is_dir: bool) -> bool { let Ok(rel) = path.strip_prefix(&self.root) else { return false }; - Self::git_internal(rel).is_none() && !self.excludes.is_excluded(rel) && !self.gitignore.is_ignored(path, is_dir) + Self::git_internal(rel).is_none() + && !self.excludes.read().is_excluded(rel) + && !self.gitignore.is_ignored(path, is_dir) } fn classify(&self, path: &Path, change: Change, out: &mut BTreeMap) { @@ -507,7 +525,7 @@ impl EventFilter { (c, _) => c, }; if let Ok(rel) = path.strip_prefix(&self.root) { - if self.excludes.is_excluded(rel) { + if self.excludes.read().is_excluded(rel) { continue; } let is_dir = meta.as_ref().is_some_and(|m| m.is_dir()); @@ -654,6 +672,9 @@ mod tests { assert!(w(".git") && w(".git/refs") && w(".git/refs/heads") && w(".git/info")); assert!(!w(".git/objects") && !w(".git/objects/ab") && !w(".git/logs")); assert!(!filter.should_watch_dir(Path::new("/elsewhere"))); + // Excludes are replaced in place (Watcher::set_excludes). + *filter.excludes.write() = ExcludeMatcher::new(&["gen".into()]); + assert!(w("node_modules") && !w("src/gen")); } fn watcher_state(w: &Watcher) -> &DirState { diff --git a/crates/ide-workspace/Cargo.toml b/crates/ide-workspace/Cargo.toml index dcbd58a4..b963c5e3 100644 --- a/crates/ide-workspace/Cargo.toml +++ b/crates/ide-workspace/Cargo.toml @@ -23,3 +23,4 @@ globset.workspace = true [dev-dependencies] tempfile.workspace = true encoding_rs.workspace = true +zip = { version = "=2.4.2", default-features = false, features = ["deflate"] } diff --git a/crates/ide-workspace/examples/open_large.rs b/crates/ide-workspace/examples/open_large.rs index 9c63ac27..9fb418fd 100644 --- a/crates/ide-workspace/examples/open_large.rs +++ b/crates/ide-workspace/examples/open_large.rs @@ -17,7 +17,8 @@ fn main() { std::fs::write(&path, &text).expect("write"); drop(text); - let docs = Documents::new(Arc::new(NullSink), Box::new(|_| LanguageId::plain_text())); + let docs = + Documents::new(Arc::new(NullSink), Box::new(|_: &std::path::Path, _: Option<&str>| LanguageId::plain_text())); let rss0 = resident_bytes().unwrap_or(0); let t = Instant::now(); let info = docs.open(&path).expect("open"); diff --git a/crates/ide-workspace/src/documents.rs b/crates/ide-workspace/src/documents.rs index 5e94f1a6..ca420936 100644 --- a/crates/ide-workspace/src/documents.rs +++ b/crates/ide-workspace/src/documents.rs @@ -46,6 +46,9 @@ pub struct TextSnapshot { pub text: String, } +/// Language of a file from its path and first line (hashbangs). +pub type DetectLanguage = dyn Fn(&Path, Option<&str>) -> LanguageId + Send + Sync; + /// Registry of open documents keyed by path, with ref-counting so split /// editors on the same file share one buffer. /// @@ -55,7 +58,7 @@ pub struct Documents { registry: Mutex, observers: RwLock>>, sink: SharedSink, - detect_language: Box LanguageId + Send + Sync>, + detect_language: Box, editorconfig: EditorConfig, defaults: RwLock, } @@ -148,7 +151,7 @@ fn read_only_error(path: &Path) -> IdeError { } impl Documents { - pub fn new(sink: SharedSink, detect_language: Box LanguageId + Send + Sync>) -> Self { + pub fn new(sink: SharedSink, detect_language: Box) -> Self { Self { registry: Mutex::default(), observers: RwLock::default(), @@ -238,13 +241,19 @@ impl Documents { } let defaults = self.file_defaults(); let loaded = ide_vfs::load_text_with_fallback(path, defaults.fallback_for(&absolute(path)))?; - let language = (self.detect_language)(path); - let meta = std::fs::metadata(path).ok(); - let writable = meta.as_ref().map(|m| !m.permissions().readonly()).unwrap_or(true); + let language = (self.detect_language)(path, loaded.text.lines().next()); + // Archive entries (`!/`) are read-only and kept from language + // servers and Local History, which only know real files. + let in_archive = ide_vfs::is_archive_path(path); + let meta = if in_archive { None } else { std::fs::metadata(path).ok() }; + let writable = !in_archive && meta.as_ref().map(|m| !m.permissions().readonly()).unwrap_or(true); let empty_file = meta.as_ref().is_some_and(|m| m.len() == 0); - let observed = !(loaded.binary || loaded.too_large); - let editorconfig = - if loaded.binary || !defaults.editorconfig { None } else { self.editorconfig.resolve(&absolute(path)) }; + let observed = !(loaded.binary || loaded.too_large || in_archive); + let editorconfig = if loaded.binary || in_archive || !defaults.editorconfig { + None + } else { + self.editorconfig.resolve(&absolute(path)) + }; let mut doc = Document::new(&loaded.text); let (mut encoding, mut has_bom) = (loaded.encoding, loaded.has_bom); // An empty file has nothing to detect: Code Style / File Encodings defaults apply. @@ -577,6 +586,37 @@ impl Documents { self.registry.lock().docs.keys().copied().collect() } + /// Detects the language of every open file again (file type associations + /// changed). A document whose language changes is reopened for the + /// observers, so it moves to the matching language servers and syntax tree, + /// and reported as [`IdeEvent::DocLanguage`]. Returns the changed documents. + pub fn redetect_languages(&self) -> Vec { + let slots: Vec<(DocId, Slot)> = self.registry.lock().docs.iter().map(|(id, e)| (*id, e.slot.clone())).collect(); + let mut changed = Vec::new(); + for (id, slot) in slots { + let mut st = slot.lock(); + if st.closed || st.uri.is_some() { + continue; + } + let text = st.doc.text(); + let language = (self.detect_language)(&st.path, text.lines().next()); + if language == st.language { + continue; + } + st.language = language.clone(); + if st.observed { + let info = Self::info_with_text(id, &st, text); + for o in self.observers() { + o.closed(id, &st.path); + o.opened(&info); + } + } + self.sink.emit(IdeEvent::DocLanguage { doc: id, language }); + changed.push(id); + } + changed + } + /// Changes the line ending used on save; marks the document dirty. pub fn set_line_ending(&self, doc: DocId, le: LineEnding) -> IdeResult<()> { self.with_doc(doc, |st| { diff --git a/crates/ide-workspace/src/file_moves.rs b/crates/ide-workspace/src/file_moves.rs index 249f3d94..dbac6285 100644 --- a/crates/ide-workspace/src/file_moves.rs +++ b/crates/ide-workspace/src/file_moves.rs @@ -241,7 +241,7 @@ mod tests { use std::sync::Arc; fn documents() -> Documents { - Documents::new(Arc::new(|_| {}), Box::new(|_: &Path| LanguageId::new("plaintext"))) + Documents::new(Arc::new(|_| {}), Box::new(|_: &Path, _: Option<&str>| LanguageId::new("plaintext"))) } fn apply(documents: &Documents) -> impl Fn(&WorkspaceEdit) -> IdeResult<()> + '_ { diff --git a/crates/ide-workspace/src/lib.rs b/crates/ide-workspace/src/lib.rs index ab8ac759..ba006750 100644 --- a/crates/ide-workspace/src/lib.rs +++ b/crates/ide-workspace/src/lib.rs @@ -14,7 +14,7 @@ pub mod roots; pub mod settings; pub mod text_refs; -pub use documents::{DocumentInfo, Documents, TextSnapshot}; +pub use documents::{DetectLanguage, DocumentInfo, Documents, TextSnapshot}; pub use editorconfig::{EditorConfig, EditorConfigProps}; pub use file_defaults::FileDefaults; pub use file_moves::{FileMove, FileMoves, UndoOutcome}; diff --git a/crates/ide-workspace/src/recent.rs b/crates/ide-workspace/src/recent.rs index b6b40ea3..3c05e178 100644 --- a/crates/ide-workspace/src/recent.rs +++ b/crates/ide-workspace/src/recent.rs @@ -62,6 +62,20 @@ impl RecentProjects { self.persist(&items) } + /// Shows `path` as `name` (File › Rename Project…); `None` restores its folder name. + pub fn rename(&self, path: &Path, name: Option<&str>) -> IdeResult<()> { + let mut items = self.items.lock(); + let Some(item) = items.iter_mut().find(|p| p.path == path) else { return Ok(()) }; + let name = name.map(str::to_owned).unwrap_or_else(|| { + path.file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_else(|| path.display().to_string()) + }); + if item.name == name { + return Ok(()); + } + item.name = name; + self.persist(&items) + } + pub fn remove(&self, path: &Path) -> IdeResult<()> { let mut items = self.items.lock(); let before = items.len(); @@ -116,4 +130,17 @@ mod tests { std::fs::write(cfg.join("recent.json"), "not json").unwrap(); assert!(RecentProjects::load(&cfg).list().is_empty()); } + + #[test] + fn rename_shows_a_custom_name_until_reset() { + let tmp = tempfile::tempdir().unwrap(); + let recent = RecentProjects::load(tmp.path()); + recent.touch(Path::new("/p/app"), None).unwrap(); + recent.rename(Path::new("/p/app"), Some("Shop Backend")).unwrap(); + recent.rename(Path::new("/p/unknown"), Some("x")).unwrap(); + assert_eq!(RecentProjects::load(tmp.path()).list()[0].name, "Shop Backend"); + recent.rename(Path::new("/p/app"), None).unwrap(); + assert_eq!(recent.list()[0].name, "app"); + assert_eq!(recent.list().len(), 1); + } } diff --git a/crates/ide-workspace/src/settings.rs b/crates/ide-workspace/src/settings.rs index 64309df3..b7afa4b1 100644 --- a/crates/ide-workspace/src/settings.rs +++ b/crates/ide-workspace/src/settings.rs @@ -2,6 +2,7 @@ use ide_core::settings::Settings; use ide_core::{IdeError, IdeResult, project_dir}; use parking_lot::RwLock; use serde_json::Value as Json; +use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use toml_edit::{DocumentMut, Item, Table, TableLike}; @@ -230,6 +231,52 @@ impl SettingsStore { *self.effective.write() = settings.clone(); settings } + + /// File › Manage IDE Settings › Export Settings…: the global layer plus + /// frontend-held IDE state (`storage`: keymaps' companions such as live + /// templates, macros) as one JSON file. + pub fn export_bundle(&self, path: &Path, storage: BTreeMap) -> IdeResult<()> { + let settings = match std::fs::read_to_string(&self.global_file) { + Ok(t) => t, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => String::new(), + Err(e) => return Err(e.into()), + }; + let bundle = SettingsBundle { format: BUNDLE_FORMAT.into(), version: BUNDLE_VERSION, settings, storage }; + let json = serde_json::to_vec_pretty(&bundle).map_err(|e| IdeError::internal(e.to_string()))?; + ide_vfs::write_atomic(path, &json)?; + Ok(()) + } + + /// Import Settings…: replaces the global layer with the bundle's (validated + /// first) and returns the new effective settings and the bundle's `storage`. + pub fn import_bundle(&self, path: &Path) -> IdeResult<(Settings, BTreeMap)> { + let bytes = std::fs::read(path)?; + let bundle: SettingsBundle = serde_json::from_slice(&bytes) + .map_err(|e| IdeError::invalid(format!("not an OpenIDEA settings file: {e}")))?; + if bundle.format != BUNDLE_FORMAT || bundle.version > BUNDLE_VERSION { + return Err(IdeError::invalid("not an OpenIDEA settings file, or one from a newer version")); + } + let layer = parse_layer(&bundle.settings).map_err(|e| IdeError::invalid(format!("invalid settings: {e}")))?; + try_merge(&mut defaults_json(), layer).map_err(|e| IdeError::invalid(format!("invalid settings: {e}")))?; + if let Some(dir) = self.global_file.parent() { + std::fs::create_dir_all(dir)?; + } + ide_vfs::write_atomic(&self.global_file, bundle.settings.as_bytes())?; + Ok((self.reload(), bundle.storage)) + } +} + +const BUNDLE_FORMAT: &str = "openidea-settings"; +const BUNDLE_VERSION: u32 = 1; + +#[derive(serde::Serialize, serde::Deserialize)] +struct SettingsBundle { + format: String, + version: u32, + /// The global `settings.toml`, verbatim (comments included). + settings: String, + #[serde(default)] + storage: BTreeMap, } #[cfg(test)] @@ -320,4 +367,68 @@ mod tests { let reloaded = SettingsStore::load(&cfg, Some(&proj)).effective(); assert_eq!(reloaded, store.effective()); } + + #[test] + fn inspection_profiles_live_in_their_layers() { + let tmp = tempfile::tempdir().unwrap(); + let cfg = tmp.path().join("cfg"); + let proj = tmp.path().join("proj"); + std::fs::create_dir_all(&cfg).unwrap(); + let store = SettingsStore::load(&cfg, Some(&proj)); + store.set(SettingsScope::Global, "inspections.default", json!({"lsp:ruff:F401": {"enabled": false}})).unwrap(); + store.set(SettingsScope::Project, "inspections.project", json!({"lsp:ruff": {"severity": "typo"}})).unwrap(); + let s = store.set(SettingsScope::Project, "inspections.profile", json!("project")).unwrap(); + assert_eq!(s.inspections.profile, "project"); + assert_eq!(s.inspections.default["lsp:ruff:F401"].enabled, Some(false)); + assert_eq!(s.inspections.project["lsp:ruff"].severity, Some(ide_core::settings::InspectionLevel::Typo)); + assert!(store.set(SettingsScope::Project, "inspections.project", json!({"x": {"severity": "loud"}})).is_err()); + let global = std::fs::read_to_string(cfg.join("settings.toml")).unwrap(); + assert!(!global.contains("profile"), "{global}"); + } + + #[test] + fn settings_bundle_round_trips_and_rejects_invalid_files() { + let tmp = tempfile::tempdir().unwrap(); + let (a, b) = (tmp.path().join("a"), tmp.path().join("b")); + std::fs::create_dir_all(&a).unwrap(); + let source = SettingsStore::load(&a, None); + source.set(SettingsScope::Global, "editor.fontSize", json!(17)).unwrap(); + source.set(SettingsScope::Global, "general.powerSaveMode", json!(true)).unwrap(); + let file = tmp.path().join("settings.json"); + let storage = BTreeMap::from([("openidea.macros.v1".to_owned(), "[]".to_owned())]); + source.export_bundle(&file, storage.clone()).unwrap(); + + let target = SettingsStore::load(&b, None); + let (settings, imported) = target.import_bundle(&file).unwrap(); + assert_eq!(settings.editor.font_size, 17.0); + assert!(settings.general.power_save_mode); + assert_eq!(imported, storage); + assert_eq!(target.effective(), source.effective()); + + let bad = tmp.path().join("bad.json"); + std::fs::write(&bad, r#"{"format":"openidea-settings","version":1,"settings":"[editor]\nfontSize = \"x\"\n"}"#) + .unwrap(); + assert!(target.import_bundle(&bad).is_err()); + std::fs::write(&bad, r#"{"format":"other","version":1,"settings":""}"#).unwrap(); + assert!(target.import_bundle(&bad).is_err()); + assert_eq!(target.effective().editor.font_size, 17.0); + } + + #[test] + fn main_toolbar_can_be_set_and_reset() { + let tmp = tempfile::tempdir().unwrap(); + let store = SettingsStore::load(tmp.path(), None); + let s = store + .set( + SettingsScope::Global, + "appearance.mainToolbar", + json!({"left": ["project"], "right": [], "hidden": ["vcs.branch"]}), + ) + .unwrap(); + let toolbar = s.appearance.main_toolbar.unwrap(); + assert_eq!(toolbar.left, ["project"]); + assert_eq!(toolbar.hidden, ["vcs.branch"]); + let s = store.set(SettingsScope::Global, "appearance.mainToolbar", Json::Null).unwrap(); + assert_eq!(s.appearance.main_toolbar, None); + } } diff --git a/crates/ide-workspace/tests/documents.rs b/crates/ide-workspace/tests/documents.rs index 30176e3a..04614dea 100644 --- a/crates/ide-workspace/tests/documents.rs +++ b/crates/ide-workspace/tests/documents.rs @@ -47,7 +47,7 @@ fn fixture() -> Fixture { }; let docs = Documents::new( sink, - Box::new(|p: &Path| match p.extension().and_then(|e| e.to_str()) { + Box::new(|p: &Path, _: Option<&str>| match p.extension().and_then(|e| e.to_str()) { Some("rs") => LanguageId::new("rust"), _ => LanguageId::plain_text(), }), @@ -61,6 +61,51 @@ fn change(from: u32, to: u32, insert: &str) -> ChangeSet { ChangeSet { changes: vec![TextChange { from, to, insert: insert.into() }] } } +#[test] +fn redetect_reopens_documents_whose_language_changed() { + let tmp = tempfile::tempdir().unwrap(); + let events: Arc>> = Arc::default(); + let sink: SharedSink = { + let events = events.clone(); + Arc::new(move |e: IdeEvent| events.lock().push(e)) + }; + let foo_is_python = Arc::new(Mutex::new(false)); + let docs = { + let foo_is_python = foo_is_python.clone(); + Documents::new( + sink, + Box::new(move |p: &Path, first: Option<&str>| { + if p.extension().is_some_and(|e| e == "foo") && *foo_is_python.lock() { + LanguageId::new("python") + } else if first.is_some_and(|l| l.starts_with("#!")) { + LanguageId::new("shellscript") + } else { + LanguageId::plain_text() + } + }), + ) + }; + let rec = Arc::new(Recorder::default()); + docs.add_observer(rec.clone()); + let foo = tmp.path().join("a.foo"); + let script = tmp.path().join("run"); + fs::write(&foo, "x = 1\n").unwrap(); + fs::write(&script, "#!/bin/sh\necho\n").unwrap(); + let a = docs.open(&foo).unwrap(); + assert_eq!(a.language.as_str(), "plaintext"); + assert_eq!(docs.open(&script).unwrap().language.as_str(), "shellscript", "the first line is detected"); + + assert!(docs.redetect_languages().is_empty()); + *foo_is_python.lock() = true; + assert_eq!(docs.redetect_languages(), vec![a.id]); + assert_eq!(docs.snapshot(a.id).unwrap().language.as_str(), "python"); + let log = rec.log.lock().clone(); + assert_eq!(log[log.len() - 2..], ["close".to_owned(), "open v0 python".to_owned()]); + assert!(events.lock().iter().any( + |e| matches!(e, IdeEvent::DocLanguage { doc, language } if *doc == a.id && language.as_str() == "python") + )); +} + fn dirty_events(events: &[IdeEvent]) -> Vec { events.iter().filter_map(|e| if let IdeEvent::DocDirty { dirty, .. } = e { Some(*dirty) } else { None }).collect() } @@ -450,3 +495,20 @@ fn toggling_the_writable_bit_updates_the_open_document() { assert!(f.docs.set_writable(&other, false).unwrap()); assert!(fs::metadata(&other).unwrap().permissions().readonly()); } + +#[test] +fn archive_entries_open_read_only_without_observers() { + use std::io::Write; + let f = fixture(); + let jar = f.root.join("lib-sources.jar"); + let mut w = zip::ZipWriter::new(fs::File::create(&jar).unwrap()); + w.start_file("org/x/Main.rs", zip::write::SimpleFileOptions::default()).unwrap(); + w.write_all(b"fn main() {}\n").unwrap(); + w.finish().unwrap(); + let path = ide_vfs::archive_entry_path(&jar, "org/x/Main.rs"); + let info = f.docs.open(&path).unwrap(); + assert_eq!((info.text.as_str(), info.language.as_str(), info.read_only), ("fn main() {}\n", "rust", true)); + assert_eq!(*info.path, *path); + assert!(f.rec.log.lock().is_empty(), "language servers / history never see archive entries"); + f.docs.close(info.id); +} diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index df445e1f..795196c0 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -54,6 +54,7 @@ ide-profiler.workspace = true ide-http.workspace = true ide-db.workspace = true ide-web.workspace = true +ide-spell.workspace = true ide-tools = { path = "../crates/ide-tools" } ide-plugin = { path = "../crates/ide-plugin" } ide-docker = { path = "../crates/ide-docker" } diff --git a/src-tauri/src/commands/analysis.rs b/src-tauri/src/commands/analysis.rs index 76b9cfae..3646f831 100644 --- a/src-tauri/src/commands/analysis.rs +++ b/src-tauri/src/commands/analysis.rs @@ -189,7 +189,7 @@ pub async fn analysis_locate_duplicates( ) -> IdeResult { let project = state.project(window.label())?; let cancel = state.begin_request(request); - let languages = state.languages.clone(); + let languages = project.languages.clone(); let syntax = state.syntax(); let result = blocking(move || { let files = files_in_scope(&project, &scope, &cancel)?; @@ -256,7 +256,7 @@ pub async fn structural_search( ) -> IdeResult<()> { let project = state.project(window.label())?; let cancel = state.begin_request(request); - let languages = state.languages.clone(); + let languages = project.languages.clone(); let syntax = state.syntax(); let events = on_event.clone(); let result = blocking(move || { diff --git a/src-tauri/src/commands/app.rs b/src-tauri/src/commands/app.rs index 4aa1ff34..4a48827e 100644 --- a/src-tauri/src/commands/app.rs +++ b/src-tauri/src/commands/app.rs @@ -69,14 +69,33 @@ pub async fn open_project(window: Window, path: PathBuf) -> IdeResult ProjectInfo { + let settings = project.settings.effective(); + let custom = settings.general.project_name.trim(); ProjectInfo { - name: project.root.file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_default(), + name: if custom.is_empty() { + project.root.file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_default() + } else { + custom.to_owned() + }, is_git: !project.repos.read().is_empty(), - settings: project.settings.effective(), + settings, root: project.root.clone().into(), } } +/// File › Rename Project…: the project's display name (title bar, recent +/// projects); an empty `name` restores the folder name. Stored in the project's settings. +#[tauri::command] +#[specta::specta] +pub fn project_rename(window: Window, state: State<'_, AppState>, name: String) -> IdeResult { + let project = state.project(window.label())?; + let name = name.trim(); + let value = if name.is_empty() { serde_json::Value::Null } else { serde_json::Value::String(name.to_owned()) }; + project.settings.set(SettingsScope::Project, "general.projectName", value)?; + state.recent.rename(&project.root, (!name.is_empty()).then_some(name))?; + Ok(project_info(&project)) +} + #[tauri::command] #[specta::specta] pub fn close_project(window: Window, state: State<'_, AppState>) { @@ -156,6 +175,15 @@ pub fn settings_set( .set_file_defaults(ide_workspace::FileDefaults::from_settings(&effective, Some(&project.root))); } } + // File type associations re-detect open documents (off the command thread). + if key == "files" || key.starts_with("files.fileType") { + let affected = if project_scope { current.iter().cloned().collect() } else { state.projects() }; + tauri::async_runtime::spawn_blocking(move || { + for project in affected { + project.apply_file_types(&project.settings.effective()); + } + }); + } // A new interpreter / toolchain / server executable restarts the language servers it affects. if ["languages", "tools"].iter().any(|k| key == *k || key.starts_with(&format!("{k}."))) { let affected = if project_scope { current.iter().cloned().collect() } else { state.projects() }; @@ -165,6 +193,16 @@ pub fn settings_set( } } let roots_changed = key == "projectStructure" || key.starts_with("projectStructure."); + // Content roots and excludes (files.exclude, ignored names, excluded folders) reach the + // index and the watchers without reopening the project (the rescan runs in the background). + if roots_changed || key == "files" || key.starts_with("files.") { + let affected = if project_scope { current.iter().cloned().collect() } else { state.projects() }; + tauri::async_runtime::spawn_blocking(move || { + for project in affected { + project.refresh_index_scope(); + } + }); + } // A project value concerns only that project's window (and its settings window). let event = ide_core::IdeEvent::SettingsChanged { key }; if project_scope { @@ -182,6 +220,48 @@ pub fn settings_set( Ok(settings) } +/// File › Manage IDE Settings › Export Settings…: the global settings plus the +/// frontend's IDE-level `storage` (live / file templates, macros) to `path`. +#[tauri::command] +#[specta::specta] +pub async fn settings_export( + state: State<'_, AppState>, + path: PathBuf, + storage: std::collections::BTreeMap, +) -> IdeResult<()> { + state.global_settings.export_bundle(&path, storage) +} + +#[derive(Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct SettingsImport { + /// Effective settings of the calling window after the import. + pub settings: Settings, + /// IDE state the frontend keeps (written back to its storage). + pub storage: std::collections::BTreeMap, +} + +/// Import Settings…: replaces the global settings with the file's; every window re-reads them. +#[tauri::command] +#[specta::specta] +pub async fn settings_import(window: Window, state: State<'_, AppState>, path: PathBuf) -> IdeResult { + let (_, storage) = state.global_settings.import_bundle(&path)?; + for project in state.projects() { + let effective = project.settings.reload(); + project + .documents + .set_file_defaults(ide_workspace::FileDefaults::from_settings(&effective, Some(&project.root))); + let lsp = project.lsp.clone(); + tauri::async_runtime::spawn(async move { lsp.reconfigure().await }); + } + state.sink.emit(ide_core::IdeEvent::SettingsChanged { key: String::new() }); + let settings = match state.project(window.label()) { + Ok(p) => p.settings.effective(), + Err(_) => state.global_settings.effective(), + }; + Ok(SettingsImport { settings, storage }) +} + /// Effective source / test / resource / excluded roots of the window's project: /// auto-detected from build files, overridden by Settings › Project Structure. #[tauri::command] diff --git a/src-tauri/src/commands/coverage.rs b/src-tauri/src/commands/coverage.rs index 2ca7f9d1..8fe66f2d 100644 --- a/src-tauri/src/commands/coverage.rs +++ b/src-tauri/src/commands/coverage.rs @@ -1,7 +1,7 @@ //! Run with Coverage: launch plans and coverage reports. use crate::state::AppState; -use ide_core::{IdeError, IdeResult}; +use ide_core::{IdeError, IdeResult, IpcPath}; use ide_coverage::{CoveragePrepared, CoverageReportSpec, CoverageSuite}; use ide_run::RunConfig; use tauri::{State, Window}; @@ -37,3 +37,12 @@ pub async fn coverage_prepare( pub async fn coverage_collect(report: CoverageReportSpec) -> IdeResult { blocking(move || ide_coverage::collect(&report)).await } + +/// Reads a report collected elsewhere (CI): lcov, a Go cover profile or JaCoCo XML, +/// with paths mapped into this project. +#[tauri::command] +#[specta::specta] +pub async fn coverage_import(window: Window, state: State<'_, AppState>, path: IpcPath) -> IdeResult { + let root = state.project(window.label())?.root.clone(); + blocking(move || ide_coverage::import(&path, &root)).await +} diff --git a/src-tauri/src/commands/doc.rs b/src-tauri/src/commands/doc.rs index 719dcbca..0a69fad3 100644 --- a/src-tauri/src/commands/doc.rs +++ b/src-tauri/src/commands/doc.rs @@ -102,22 +102,32 @@ pub fn doc_set_line_ending( state.project(window.label())?.documents.set_line_ending(doc, line_ending) } -/// Structure view: LSP document symbols when available, tree-sitter otherwise. +/// Structure view: LSP document symbols when available (with tree-sitter +/// visibility), tree-sitter otherwise. /// Tree-sitter answers come from the per-document tree cache (incremental /// re-parse); large documents get no syntax structure. #[tauri::command] #[specta::specta] pub async fn doc_outline(window: Window, state: State<'_, AppState>, doc: DocId) -> IdeResult> { let project = state.project(window.label())?; - if let Ok(Some(items)) = project.lsp.document_symbols(doc).await { - return Ok(items); - } + let lsp = project.lsp.document_symbols(doc).await.ok().flatten(); blocking(move || { - let snap = project.documents.text_snapshot(doc)?; - if snap.large { - return Ok(Vec::new()); - } - Ok(project.syntax.outline(doc, &snap.language, snap.version, &snap.text)) + let snap = match project.documents.text_snapshot(doc) { + Ok(snap) => snap, + Err(e) => return lsp.ok_or(e), + }; + let syntax = || project.syntax.outline(doc, &snap.language, snap.version, &snap.text); + Ok(match lsp { + // Server symbols carry no visibility; borrow it from the syntax tree. + Some(mut items) => { + if !snap.large { + ide_language::merge_visibility(&mut items, &syntax()); + } + items + } + None if snap.large => Vec::new(), + None => syntax(), + }) }) .await } diff --git a/src-tauri/src/commands/docker.rs b/src-tauri/src/commands/docker.rs index ebeb116c..aecac0a7 100644 --- a/src-tauri/src/commands/docker.rs +++ b/src-tauri/src/commands/docker.rs @@ -2,7 +2,10 @@ use crate::state::AppState; use ide_core::{IdeError, IdeResult, RequestId}; -use ide_docker::{ComposeOp, ContainerOp, DockerEvent, DockerLogLine, DockerObject, DockerSnapshot, PruneTarget}; +use ide_docker::{ + ComposeOp, ContainerOp, DockerEndpoint, DockerEvent, DockerLogLine, DockerObject, DockerRegistry, DockerSettings, + DockerSnapshot, PruneTarget, RegistryAuth, +}; use tauri::State; use tauri::ipc::Channel; @@ -25,35 +28,42 @@ async fn cancellable( /// Availability plus containers, images, volumes and networks. #[tauri::command] #[specta::specta] -pub async fn docker_snapshot() -> IdeResult { - blocking(|| Ok(ide_docker::snapshot())).await +pub async fn docker_snapshot(endpoint: DockerEndpoint) -> IdeResult { + blocking(move || Ok(ide_docker::snapshot(&endpoint))).await } #[tauri::command] #[specta::specta] -pub async fn docker_container_op(ids: Vec, op: ContainerOp) -> IdeResult<()> { - blocking(move || ide_docker::container_op(&ids, op)).await +pub async fn docker_container_op(endpoint: DockerEndpoint, ids: Vec, op: ContainerOp) -> IdeResult<()> { + blocking(move || ide_docker::container_op(&endpoint, &ids, op)).await } #[tauri::command] #[specta::specta] -pub async fn docker_remove(object: DockerObject, ids: Vec, force: bool) -> IdeResult<()> { - blocking(move || ide_docker::remove(object, &ids, force)).await +pub async fn docker_remove( + endpoint: DockerEndpoint, + object: DockerObject, + ids: Vec, + force: bool, +) -> IdeResult<()> { + blocking(move || ide_docker::remove(&endpoint, object, &ids, force)).await } /// Pretty-printed `docker inspect` JSON. #[tauri::command] #[specta::specta] -pub async fn docker_inspect(object: DockerObject, id: String) -> IdeResult { - blocking(move || ide_docker::inspect(object, &id)).await +pub async fn docker_inspect(endpoint: DockerEndpoint, object: DockerObject, id: String) -> IdeResult { + blocking(move || ide_docker::inspect(&endpoint, object, &id)).await } /// Streams a container's log lines until it stops or the request is cancelled. #[tauri::command] #[specta::specta] +#[allow(clippy::too_many_arguments)] pub async fn docker_logs( state: State<'_, AppState>, request: RequestId, + endpoint: DockerEndpoint, id: String, tail: u32, follow: bool, @@ -61,7 +71,7 @@ pub async fn docker_logs( on_line: Channel, ) -> IdeResult<()> { cancellable(&state, request, move |cancel| { - ide_docker::logs(&id, tail, follow, timestamps, cancel, &|line| { + ide_docker::logs(&endpoint, &id, tail, follow, timestamps, cancel, &|line| { let _ = on_line.send(line); }) }) @@ -74,26 +84,31 @@ pub async fn docker_logs( pub async fn docker_events( state: State<'_, AppState>, request: RequestId, + endpoint: DockerEndpoint, on_event: Channel, ) -> IdeResult<()> { cancellable(&state, request, move |cancel| { - ide_docker::watch_events(cancel, &|e| { + ide_docker::watch_events(&endpoint, cancel, &|e| { let _ = on_event.send(e); }) }) .await } +/// `docker pull`, logging in first when a saved registry with a password serves the image. #[tauri::command] #[specta::specta] pub async fn docker_pull( state: State<'_, AppState>, request: RequestId, + endpoint: DockerEndpoint, reference: String, on_output: Channel, ) -> IdeResult<()> { + let data_dir = state.data_dir.clone(); cancellable(&state, request, move |cancel| { - ide_docker::pull(&reference, cancel, &|line| { + let auth = ide_docker::auth_for(&data_dir, &reference); + ide_docker::pull(&endpoint, &reference, auth.as_ref(), cancel, &|line| { let _ = on_output.send(line); }) }) @@ -105,11 +120,12 @@ pub async fn docker_pull( pub async fn docker_prune( state: State<'_, AppState>, request: RequestId, + endpoint: DockerEndpoint, target: PruneTarget, on_output: Channel, ) -> IdeResult<()> { cancellable(&state, request, move |cancel| { - ide_docker::prune(target, cancel, &|line| { + ide_docker::prune(&endpoint, target, cancel, &|line| { let _ = on_output.send(line); }) }) @@ -123,6 +139,7 @@ pub async fn docker_prune( pub async fn docker_compose_op( state: State<'_, AppState>, request: RequestId, + endpoint: DockerEndpoint, project: String, files: Vec, working_dir: Option, @@ -130,9 +147,65 @@ pub async fn docker_compose_op( on_output: Channel, ) -> IdeResult<()> { cancellable(&state, request, move |cancel| { - ide_docker::compose_op(&project, &files, working_dir.as_deref(), op, cancel, &|line| { + ide_docker::compose_op(&endpoint, &project, &files, working_dir.as_deref(), op, cancel, &|line| { let _ = on_output.send(line); }) }) .await } + +/// Docker connections and registries (app-wide). +#[tauri::command] +#[specta::specta] +pub async fn docker_settings(state: State<'_, AppState>) -> IdeResult { + let data_dir = state.data_dir.clone(); + blocking(move || Ok(ide_docker::load_settings(&data_dir))).await +} + +#[tauri::command] +#[specta::specta] +pub async fn docker_save_settings(state: State<'_, AppState>, settings: DockerSettings) -> IdeResult<()> { + let data_dir = state.data_dir.clone(); + blocking(move || ide_docker::save_settings(&data_dir, &settings)).await +} + +/// Saves (`None`: forgets) a registry's password. +#[tauri::command] +#[specta::specta] +pub async fn docker_set_registry_password( + state: State<'_, AppState>, + id: String, + password: Option, +) -> IdeResult<()> { + let data_dir = state.data_dir.clone(); + blocking(move || ide_docker::set_registry_password(&data_dir, &id, password.as_deref())).await +} + +/// Test login to `registry` (`password`, else the saved one) through `endpoint`'s daemon. +#[tauri::command] +#[specta::specta] +pub async fn docker_registry_login( + state: State<'_, AppState>, + endpoint: DockerEndpoint, + registry: DockerRegistry, + password: Option, +) -> IdeResult { + let data_dir = state.data_dir.clone(); + blocking(move || { + let password = password + .or_else(|| ide_docker::registry_password(&data_dir, ®istry.id)) + .ok_or_else(|| IdeError::invalid("enter the registry password"))?; + let auth = RegistryAuth { address: registry.address, username: registry.username, password }; + ide_docker::login(&endpoint, &auth) + }) + .await +} + +/// Environment for a Docker run configuration's command line: selects `endpoint`'s +/// daemon and puts the docker executable on `PATH`. +#[tauri::command] +#[specta::specta] +pub async fn docker_run_env(endpoint: DockerEndpoint) -> IdeResult> { + endpoint.validate()?; + blocking(move || Ok(ide_docker::run_env(&endpoint))).await +} diff --git a/src-tauri/src/commands/lsp.rs b/src-tauri/src/commands/lsp.rs index 75e3859b..9a11ac0b 100644 --- a/src-tauri/src/commands/lsp.rs +++ b/src-tauri/src/commands/lsp.rs @@ -26,6 +26,24 @@ pub async fn lsp_completion( result } +/// Inline (ghost text) suggestions at `pos`; `automatic` when triggered by typing. +#[tauri::command] +#[specta::specta] +pub async fn lsp_inline_completion( + window: Window, + state: State<'_, AppState>, + request: RequestId, + doc: DocId, + pos: Pos, + automatic: bool, +) -> IdeResult> { + let lsp = project!(window, state).lsp.clone(); + let cancel = state.begin_request(request); + let result = lsp.inline_completion(doc, pos, automatic, cancel).await; + state.end_request(request); + result +} + #[tauri::command] #[specta::specta] pub async fn lsp_resolve_completion( diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index da71eee9..2a312c05 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -30,6 +30,7 @@ pub mod refactor; pub mod run; pub mod sdk; pub mod search; +pub mod spell; pub mod syntax; pub mod term; pub mod tools; @@ -51,6 +52,9 @@ pub fn builder() -> tauri_specta::Builder { app::settings_get, app::settings_get_global, app::settings_set, + app::settings_export, + app::settings_import, + app::project_rename, app::project_roots, app::cancel_request, app::log_frontend, @@ -108,11 +112,14 @@ pub fn builder() -> tauri_specta::Builder { nav::fs_resolve_reference, nav::path_completions, nav::web_unresolved_references, + spell::spell_check, + spell::spell_suggest, search::search_start, search::replace_in_files, search::replace_preview, search::search_validate, lsp::lsp_completion, + lsp::lsp_inline_completion, lsp::lsp_resolve_completion, lsp::lsp_hover, lsp::lsp_signature_help, @@ -170,12 +177,20 @@ pub fn builder() -> tauri_specta::Builder { vcs::vcs_diff_hunks, vcs::vcs_stage_hunks, vcs::vcs_clone, + vcs::vcs_init, vcs::vcs_git_version, vcs::vcs_detected_git, vcs::vcs_config_get, vcs::vcs_operation, vcs::vcs_create_tag, vcs::vcs_tags, + vcs::vcs_merge_with, + vcs::vcs_rebase_with, + vcs::vcs_remotes, + vcs::vcs_edit_remote, + vcs::vcs_worktrees, + vcs::vcs_add_worktree, + vcs::vcs_remove_worktree, vcs::vcs_diff_with_worktree, vcs::vcs_diff_revisions, vcs::vcs_create_patch, @@ -199,11 +214,15 @@ pub fn builder() -> tauri_specta::Builder { term::term_available_shells, run::run_configs, run::run_save_configs, + run::run_templates, + run::run_save_templates, + run::run_system_env, run::run_start, run::run_stop, run::run_stdin, run::run_list, run::run_close_stdin, + run::run_resize, run::run_test_reports, build::build_model, build::build_sync, @@ -231,6 +250,7 @@ pub fn builder() -> tauri_specta::Builder { tools::tools_uninstall, sdk::sdk_detect, sdk::sdk_python_effective, + sdk::sdk_project_libraries, sdk::sdk_locate_tools, sdk::sdk_python_create_venv, sdk::sdk_python_packages, @@ -248,6 +268,7 @@ pub fn builder() -> tauri_specta::Builder { window::menu_configure, window::menu_refresh, window::menu_model, + window::menu_set_disabled, window::app_log_location, window::app_build_info, window::app_check_update, @@ -268,6 +289,7 @@ pub fn builder() -> tauri_specta::Builder { debug::debug_redefine_classes, coverage::coverage_prepare, coverage::coverage_collect, + coverage::coverage_import, web::web_browsers, web::web_url, web::web_open, @@ -299,6 +321,11 @@ pub fn builder() -> tauri_specta::Builder { docker::docker_pull, docker::docker_prune, docker::docker_compose_op, + docker::docker_settings, + docker::docker_save_settings, + docker::docker_set_registry_password, + docker::docker_registry_login, + docker::docker_run_env, github::gh_availability, github::gh_pr_list, github::gh_pr_view, diff --git a/src-tauri/src/commands/run.rs b/src-tauri/src/commands/run.rs index 693a9d98..a95d00ed 100644 --- a/src-tauri/src/commands/run.rs +++ b/src-tauri/src/commands/run.rs @@ -26,6 +26,28 @@ pub fn run_save_configs(window: Window, state: State<'_, AppState>, configs: Vec ide_run::save_user_configs(&root, &configs) } +/// Configuration templates: per-type defaults applied to new configurations. +#[tauri::command] +#[specta::specta] +pub fn run_templates(window: Window, state: State<'_, AppState>) -> IdeResult> { + let root = state.project(window.label())?.root.clone(); + ide_run::load_templates(&root) +} + +#[tauri::command] +#[specta::specta] +pub fn run_save_templates(window: Window, state: State<'_, AppState>, templates: Vec) -> IdeResult<()> { + let root = state.project(window.label())?.root.clone(); + ide_run::save_templates(&root, &templates) +} + +/// The environment runs inherit when "Include system environment variables" is on. +#[tauri::command] +#[specta::specta] +pub fn run_system_env() -> std::collections::BTreeMap { + ide_run::system_env() +} + #[tauri::command] #[specta::specta] pub fn run_start( @@ -69,6 +91,13 @@ pub fn run_stdin(state: State<'_, AppState>, id: RunId, data: String) -> IdeResu state.processes.write_stdin(id, &data) } +/// Terminal size of a run started with "Emulate terminal in output console". +#[tauri::command] +#[specta::specta] +pub fn run_resize(state: State<'_, AppState>, id: RunId, cols: u16, rows: u16) -> IdeResult<()> { + state.processes.resize(id, cols, rows) +} + #[tauri::command] #[specta::specta] pub fn run_list(state: State<'_, AppState>) -> Vec { diff --git a/src-tauri/src/commands/sdk.rs b/src-tauri/src/commands/sdk.rs index c92d6eef..009ae004 100644 --- a/src-tauri/src/commands/sdk.rs +++ b/src-tauri/src/commands/sdk.rs @@ -28,6 +28,33 @@ pub fn sdk_python_effective(window: Window, state: State<'_, AppState>) -> IdeRe Ok(ide_sdk::apply::python_interpreter(&project.root, &project.settings.effective()).map(Into::into)) } +/// SDK / library source folders shown under "External Libraries" in the Project view, +/// with the Maven / Gradle dependency jars of the build model. +#[tauri::command] +#[specta::specta] +pub async fn sdk_project_libraries( + window: Window, + state: State<'_, AppState>, +) -> IdeResult> { + let project = state.project(window.label())?; + let (root, cache, settings) = (project.root.clone(), project.build_cache_dir(), project.settings.effective()); + tauri::async_runtime::spawn_blocking(move || { + let model = crate::commands::build::load(&root, &cache, &settings); + let deps: Vec = ide_build::dependency_coordinates(&root, &model) + .into_iter() + .map(|c| ide_sdk::libraries::BuildDependency { + gradle: c.gradle, + group: c.group_id, + artifact: c.artifact_id, + version: c.version, + }) + .collect(); + ide_sdk::libraries::project_libraries(&root, &settings, &deps) + }) + .await + .map_err(join_err) +} + /// Executables found for "run this file", and where single files are compiled to. #[derive(Clone, Debug, serde::Serialize, specta::Type)] #[serde(rename_all = "camelCase")] diff --git a/src-tauri/src/commands/search.rs b/src-tauri/src/commands/search.rs index 3b5d3265..212014d5 100644 --- a/src-tauri/src/commands/search.rs +++ b/src-tauri/src/commands/search.rs @@ -40,7 +40,14 @@ pub async fn search_start( let cancel = state.begin_request(request); let documents = project.documents.clone(); let root = project.root.clone(); - let languages = state.languages.clone(); + // The whole project also covers the Project Structure content roots. + let content_roots = match query.scope { + ide_search::SearchScope::Project | ide_search::SearchScope::FileSet { set: ProjectFileSet::All } => { + project.content_roots() + } + _ => Vec::new(), + }; + let languages = project.languages.clone(); let syntax = state.syntax(); let events = on_event.clone(); let summary = tauri::async_runtime::spawn_blocking(move || { @@ -55,17 +62,29 @@ pub async fn search_start( let found = syntax.literals(&language, text)?; Some(found.into_iter().map(literal_range).collect()) }; - ide_search::search_in_context( - &root, - &query, - Some(&filter), - Some(&literals), - &|p| documents.text_of(p), - &cancel, - &|file| { - let _ = events.send(SearchEvent::File { file }); - }, - ) + let mut total = ide_search::SearchSummary::default(); + for dir in std::iter::once(&root).chain(&content_roots) { + let s = ide_search::search_in_context( + dir, + &query, + Some(&filter), + Some(&literals), + &|p| documents.text_of(p), + &cancel, + &|file| { + let _ = events.send(SearchEvent::File { file }); + }, + )?; + total.files += s.files; + total.matches += s.matches; + total.elapsed_ms += s.elapsed_ms; + total.limit_hit |= s.limit_hit; + total.cancelled |= s.cancelled; + if total.limit_hit || total.cancelled { + break; + } + } + Ok::<_, ide_core::IdeError>(total) }) .await .map_err(|e| ide_core::IdeError::internal(e.to_string()))?; diff --git a/src-tauri/src/commands/spell.rs b/src-tauri/src/commands/spell.rs new file mode 100644 index 00000000..50ccba33 --- /dev/null +++ b/src-tauri/src/commands/spell.rs @@ -0,0 +1,90 @@ +//! Proofreading › Typo: spell checking of open documents. + +use super::doc::blocking; +use crate::state::{AppState, Project}; +use ide_core::settings::SpellingSettings; +use ide_core::{DocId, IdeError, IdeResult, RequestId}; +use ide_language::SpellingKind; +use ide_spell::{Region, RegionKind, Speller, Typo}; +use std::path::{Path, PathBuf}; +use tauri::{State, Window}; + +/// Languages checked as prose (all text but code spans). +const PROSE: &[&str] = &["plaintext", "markdown", "restructuredtext", "asciidoc", "latex"]; + +/// Custom dictionary paths: `~/…` from home, relative ones from the project root. +fn dictionary_paths(settings: &SpellingSettings, root: &Path) -> Vec { + settings + .custom_dictionaries + .iter() + .map(|p| p.trim()) + .filter(|p| !p.is_empty()) + .map(|p| match p.strip_prefix("~/") { + Some(rest) => dirs::home_dir().unwrap_or_default().join(rest), + None => root.join(p), + }) + .collect() +} + +fn speller(project: &Project) -> Speller { + let s = project.settings.effective().spelling; + let words = s.ide_words.iter().chain(&s.project_words); + Speller::new(words, &dictionary_paths(&s, &project.root)) +} + +/// Misspelt words in the comments, string literals and declared names of +/// `doc` (all of its text for prose). Cancel with `cancel_request(request)`. +#[tauri::command] +#[specta::specta] +pub async fn spell_check( + window: Window, + state: State<'_, AppState>, + doc: DocId, + request: RequestId, +) -> IdeResult> { + let project = state.project(window.label())?; + let syntax = state.syntax(); + let cancel = state.begin_request(request); + let result = blocking(move || { + let snap = project.documents.text_snapshot(doc)?; + if snap.large { + return Ok(Vec::new()); + } + let regions = if PROSE.contains(&snap.language.as_str()) { + ide_spell::prose_regions(&snap.text) + } else { + let Some(found) = syntax.spelling_regions(&snap.language, &snap.text) else { return Ok(Vec::new()) }; + found + .into_iter() + .map(|r| Region { + start: r.start, + end: r.end, + kind: match r.kind { + SpellingKind::Comment => RegionKind::Comment, + SpellingKind::String => RegionKind::String, + SpellingKind::Name => RegionKind::Name, + }, + }) + .collect() + }; + if cancel.is_cancelled() { + return Err(IdeError::cancelled()); + } + speller(&project).check(&snap.text, ®ions, &|| cancel.is_cancelled()).ok_or_else(IdeError::cancelled) + }) + .await; + state.end_request(request); + result +} + +/// Replacements for a misspelt `word`, best first (Typo › Change to…). +#[tauri::command] +#[specta::specta] +pub async fn spell_suggest(window: Window, state: State<'_, AppState>, word: String) -> IdeResult> { + let project = state.project(window.label()).ok(); + blocking(move || { + let speller = project.as_deref().map(speller).unwrap_or_default(); + Ok(speller.suggest(&word, 8)) + }) + .await +} diff --git a/src-tauri/src/commands/syntax.rs b/src-tauri/src/commands/syntax.rs index 591dd457..a43a3dd6 100644 --- a/src-tauri/src/commands/syntax.rs +++ b/src-tauri/src/commands/syntax.rs @@ -129,7 +129,7 @@ pub async fn syntax_identifiers_in_files( request: RequestId, ) -> IdeResult> { let project = state.project(window.label())?; - let languages = state.languages.clone(); + let languages = project.languages.clone(); let cancel = state.begin_request(request); let result = blocking(move || { let load = |path: &std::path::Path| { diff --git a/src-tauri/src/commands/vcs.rs b/src-tauri/src/commands/vcs.rs index 5dcd0057..74076289 100644 --- a/src-tauri/src/commands/vcs.rs +++ b/src-tauri/src/commands/vcs.rs @@ -374,6 +374,13 @@ pub enum RemoteOp { UpdateBranch { branch: String, }, + /// Pull dialog: `branches` of `remote` (none = the configured merge branch). + PullFrom { + remote: String, + branches: Vec, + rebase: bool, + options: MergeOptions, + }, } /// Network operation; progress lines stream through `on_output`. Cancellable. @@ -393,7 +400,7 @@ pub async fn vcs_remote( let out = |line: String| { let _ = on_output.send(line); }; - let recorded = matches!(op, RemoteOp::Pull { .. }).then_some("Git pull"); + let recorded = matches!(op, RemoteOp::Pull { .. } | RemoteOp::PullFrom { .. }).then_some("Git pull"); project.with_repo_recorded(repo.as_deref(), recorded, |r| match op { RemoteOp::Fetch => r.fetch(&cancel, &out), RemoteOp::Pull { rebase } => r.pull(rebase, &cancel, &out), @@ -408,6 +415,9 @@ pub async fn vcs_remote( None => r.push_branch(&branch, revision.as_deref(), force_with_lease, &cancel, &out), }, RemoteOp::UpdateBranch { branch } => r.update_branch(&branch, &cancel, &out), + RemoteOp::PullFrom { remote, branches, rebase, options } => { + r.pull_from(&remote, &branches, rebase, &options, &cancel, &out) + } }) }) .await @@ -486,6 +496,14 @@ pub async fn vcs_clone( result } +/// `git init` in `dir` (File › New Project… "Create Git repository"). +#[tauri::command] +#[specta::specta] +pub async fn vcs_init(state: State<'_, AppState>, dir: PathBuf) -> IdeResult<()> { + apply_git_executable(&state.global_settings.effective()); + blocking(move || ide_vcs::init(&dir)).await +} + #[derive(Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] pub struct GitExecutableInfo { @@ -629,6 +647,100 @@ pub async fn vcs_tags(window: Window, state: State<'_, AppState>, repo: Option

, + repo: Option, + branches: Vec, + options: MergeOptions, +) -> IdeResult<()> { + let project = state.project(window.label())?; + blocking(move || { + project.with_repo_recorded(repo.as_deref(), Some("Git merge"), |r| r.merge_with(&branches, &options)) + }) + .await +} + +/// Rebase dialog: rebases the current branch onto `upstream` (or `--onto`). +#[tauri::command] +#[specta::specta] +pub async fn vcs_rebase_with( + window: Window, + state: State<'_, AppState>, + repo: Option, + upstream: String, + options: RebaseOptions, +) -> IdeResult<()> { + let project = state.project(window.label())?; + blocking(move || { + project.with_repo_recorded(repo.as_deref(), Some("Git rebase"), |r| r.rebase_with(&upstream, &options)) + }) + .await +} + +#[tauri::command] +#[specta::specta] +pub async fn vcs_remotes(window: Window, state: State<'_, AppState>, repo: Option) -> IdeResult> { + let project = state.project(window.label())?; + blocking(move || project.with_repo_at(repo.as_deref(), |r| r.remotes())).await +} + +#[tauri::command] +#[specta::specta] +pub async fn vcs_edit_remote( + window: Window, + state: State<'_, AppState>, + repo: Option, + edit: RemoteEdit, +) -> IdeResult<()> { + let project = state.project(window.label())?; + blocking(move || project.with_repo_at(repo.as_deref(), |r| r.edit_remote(&edit))).await +} + +#[tauri::command] +#[specta::specta] +pub async fn vcs_worktrees( + window: Window, + state: State<'_, AppState>, + repo: Option, +) -> IdeResult> { + let project = state.project(window.label())?; + blocking(move || project.with_repo_at(repo.as_deref(), |r| r.worktrees())).await +} + +#[tauri::command] +#[specta::specta] +pub async fn vcs_add_worktree( + window: Window, + state: State<'_, AppState>, + repo: Option, + path: PathBuf, + commitish: String, + new_branch: Option, +) -> IdeResult<()> { + let project = state.project(window.label())?; + blocking(move || { + project.with_repo_at(repo.as_deref(), |r| r.add_worktree(&path, &commitish, new_branch.as_deref())) + }) + .await +} + +#[tauri::command] +#[specta::specta] +pub async fn vcs_remove_worktree( + window: Window, + state: State<'_, AppState>, + repo: Option, + path: PathBuf, + force: bool, +) -> IdeResult<()> { + let project = state.project(window.label())?; + blocking(move || project.with_repo_at(repo.as_deref(), |r| r.remove_worktree(&path, force))).await +} + /// Compare with Local: tracked files that differ between `rev` and the working tree. #[tauri::command] #[specta::specta] diff --git a/src-tauri/src/commands/window.rs b/src-tauri/src/commands/window.rs index 64725def..20ecd5d1 100644 --- a/src-tauri/src/commands/window.rs +++ b/src-tauri/src/commands/window.rs @@ -150,13 +150,29 @@ pub fn menu_refresh(app: AppHandle, menu_state: State<'_, MenuState>, state: Sta /// The menu bar for a title bar drawn by the frontend (Windows). #[tauri::command] #[specta::specta] -pub fn menu_model(app: AppHandle, state: State<'_, AppState>) -> Vec { +pub fn menu_model(app: AppHandle, state: State<'_, AppState>, menu_state: State<'_, MenuState>) -> Vec { let mut spec = menu::spec(menu::Os::current()); if !updates_configured(&app) { spec = menu::without_action(spec, "app.checkForUpdates"); } let home = dirs::home_dir(); - menu::model(&spec, &menu::recent_items(&state.recent.list(), home.as_deref())) + let config = menu_state.config.lock().clone(); + menu::model(&spec, &menu::recent_items(&state.recent.list(), home.as_deref()), &config) +} + +/// Action ids the focused window cannot run now: their native menu items are +/// disabled in place (no rebuild). +#[tauri::command] +#[specta::specta] +pub fn menu_set_disabled(app: AppHandle, menu_state: State<'_, MenuState>, ids: Vec) { + let disabled: std::collections::HashSet = ids.into_iter().collect(); + if *menu_state.disabled.lock() == disabled { + return; + } + *menu_state.disabled.lock() = disabled.clone(); + if menu::native_menu_bar() { + menu::apply_disabled(&app, &disabled); + } } #[derive(Serialize, Deserialize, Type)] diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs index 02c41f7f..b3fdce8e 100644 --- a/src-tauri/src/menu.rs +++ b/src-tauri/src/menu.rs @@ -20,15 +20,17 @@ use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use specta::Type; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::path::Path; -use tauri::menu::{IsMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu}; +use tauri::menu::{IsMenuItem, Menu, MenuItem, MenuItemKind, PredefinedMenuItem, Submenu}; use tauri::{AppHandle, Runtime}; /// Event emitted to the focused window when an IDE menu item is clicked. pub const MENU_EVENT: &str = "openidea://menu"; /// Prefix of Open Recent item ids; the rest of the id is the project path. const RECENT_PREFIX: &str = "recent:"; +/// Prefix of items the frontend lists ([`MenuConfig::dynamic`]): `dyn:` or `dyn:|`. +const DYNAMIC_PREFIX: &str = "dyn:"; /// Action run for an Open Recent entry (argument: the project path). pub const OPEN_RECENT_ACTION: &str = "file.openRecentProject"; const MAX_RECENT: usize = 15; @@ -145,6 +147,9 @@ pub enum Entry { Separator, /// Open Recent entries, filled from the recent-projects list. RecentProjects, + /// Items the frontend lists under this slot name (registered tool windows, + /// saved layouts), from [`MenuConfig::dynamic`]. + Dynamic(&'static str), } fn sub(id: &'static str, label: &'static str, children: Vec) -> Entry { @@ -161,6 +166,10 @@ fn hinted(id: &'static str, label: &'static str, hint: &'static str) -> Entry { } const SEP: Entry = Entry::Separator; +/// [`Entry::Dynamic`] slots the frontend fills. +pub const DYNAMIC_TOOL_WINDOWS: &str = "toolWindows"; +pub const DYNAMIC_LAYOUTS: &str = "toolWindowLayouts"; + /// The menu bar for `os`. Labels are English defaults; the frontend replaces /// them with `menu.` strings from the active locale catalog. pub fn spec(os: Os) -> Vec { @@ -194,6 +203,7 @@ pub fn spec(os: Os) -> Vec { } let mut file = vec![ + act("file.newProject", "New Project…"), act("file.new", "New…"), act("file.newScratchFile", "New Scratch File…"), act("file.openProject", "Open…"), @@ -204,16 +214,26 @@ pub fn spec(os: Os) -> Vec { vec![Entry::RecentProjects, SEP, act("welcome.recentProjects", "Manage Projects…")], ), act("file.closeProject", "Close Project"), + act("file.renameProject", "Rename Project…"), SEP, act("file.save", "Save All"), + act("file.reloadAllFromDisk", "Reload All from Disk"), sub("fileProperties", "File Properties", vec![act("file.toggleReadOnly", "Make File Read-only / Writable")]), SEP, + act("file.invalidateCaches", "Invalidate Caches…"), + SEP, + sub( + "manageIdeSettings", + "Manage IDE Settings", + vec![act("file.exportSettings", "Export Settings…"), act("file.importSettings", "Import Settings…")], + ), sub( "newProjectsSetup", "New Projects Setup", vec![act("file.newProjectsSettings", "Settings for New Projects…")], ), - act("file.invalidateCaches", "Invalidate Caches…"), + SEP, + act("file.togglePowerSave", "Power Save Mode"), ]; if !mac { // macOS keeps Settings… and Quit in the application menu. @@ -237,6 +257,34 @@ pub fn spec(os: Os) -> Vec { act("find.showUsages", "Show Usages"), ], ); + let macros = || { + sub( + "macros", + "Macros", + vec![ + act("macro.startRecording", "Start Macro Recording"), + act("macro.stopRecording", "Stop Macro Recording"), + act("macro.playBack", "Play Back Last Macro"), + SEP, + act("macro.playSaved", "Play Saved Macros…"), + act("macro.edit", "Edit Macros…"), + ], + ) + }; + let bookmarks = || { + sub( + "bookmarks", + "Bookmarks", + vec![ + act("navigate.toggleBookmark", "Toggle Bookmark"), + act("navigate.toggleBookmarkWithMnemonic", "Toggle Bookmark with Mnemonic"), + SEP, + act("navigate.showBookmarks", "Show All Bookmarks…"), + act("navigate.nextBookmark", "Next Bookmark"), + act("navigate.prevBookmark", "Previous Bookmark"), + ], + ) + }; let edit = if mac { vec![ Entry::Native(Native::Undo), @@ -258,6 +306,9 @@ pub fn spec(os: Os) -> Vec { act("code.reverseLines", "Reverse Lines"), SEP, act("editor.toggleColumnSelection", "Column Selection Mode"), + SEP, + macros(), + bookmarks(), ] } else { // GTK/Win32 accelerators would swallow these keys before the webview, so @@ -282,6 +333,9 @@ pub fn spec(os: Os) -> Vec { act("code.reverseLines", "Reverse Lines"), SEP, act("editor.toggleColumnSelection", "Column Selection Mode"), + SEP, + macros(), + bookmarks(), ] }; menus.push(sub("edit", "Edit", edit)); @@ -293,37 +347,39 @@ pub fn spec(os: Os) -> Vec { sub( "toolWindows", "Tool Windows", - vec![ - act("toolwindow.project", "Project"), - act("toolwindow.bookmarks", "Bookmarks"), - act("toolwindow.find", "Find"), - act("toolwindow.run", "Run"), - act("toolwindow.build", "Build"), - act("toolwindow.debug", "Debug"), - act("toolwindow.coverage", "Coverage"), - act("toolwindow.profiler", "Profiler"), - act("toolwindow.problems", "Problems"), - act("toolwindow.structure", "Structure"), - act("toolwindow.services", "Services"), - act("toolwindow.git", "Git"), - act("toolwindow.commit", "Commit"), - act("toolwindow.terminal", "Terminal"), - SEP, - act("toolwindow.hideAll", "Hide All Tool Windows"), - ], + vec![Entry::Dynamic(DYNAMIC_TOOL_WINDOWS), SEP, act("toolwindow.hideAll", "Hide All Tool Windows")], ), sub( "appearance", "Appearance", vec![ act("editor.toggleZen", "Enter Distraction Free Mode"), + act("window.toggleFullScreen", "Toggle Full Screen"), + act("view.toggleCompactMode", "Compact Mode"), + SEP, + act("view.zoomIn", "Zoom In IDE"), + act("view.zoomOut", "Zoom Out IDE"), + act("view.resetZoom", "Reset IDE Zoom"), SEP, act("view.toggleTheme", "Toggle Light/Dark Theme"), + act("toolbar.customize", "Customize Main Toolbar…"), ], ), - act("view.notifications", "Notifications"), SEP, - act("window.toggleFullScreen", "Toggle Full Screen"), + act("code.quickDefinition", "Quick Definition"), + act("code.quickTypeDefinition", "Quick Type Definition"), + SEP, + act("navigate.recentLocations", "Recent Locations"), + act("navigate.recentFiles", "Recent Files"), + act("localHistory.recentChanges", "Recent Changes"), + SEP, + act("diff.compareWithClipboard", "Compare with Clipboard"), + SEP, + act("view.quickSwitchScheme", "Quick Switch Scheme…"), + SEP, + act("editor.resetFontSize", "Reset Font Size in All Editors"), + SEP, + act("view.notifications", "Notifications"), ], )); @@ -337,8 +393,6 @@ pub fn spec(os: Os) -> Vec { act("navigate.gotoSymbol", "Symbol…"), act("navigate.gotoLine", "Line/Column…"), SEP, - act("navigate.recentFiles", "Recent Files"), - act("navigate.recentLocations", "Recent Locations"), act("navigate.back", "Back"), act("navigate.forward", "Forward"), act("navigate.lastEditLocation", "Last Edit Location"), @@ -360,8 +414,6 @@ pub fn spec(os: Os) -> Vec { act("navigate.nextError", "Next Highlighted Error"), act("navigate.prevError", "Previous Highlighted Error"), SEP, - act("navigate.toggleBookmark", "Toggle Bookmark"), - act("navigate.showBookmarks", "Show Bookmarks"), act("navigate.selectIn", "Select In…"), ], )); @@ -394,6 +446,7 @@ pub fn spec(os: Os) -> Vec { SEP, act("code.reformat", "Reformat Code"), act("code.optimizeImports", "Optimize Imports"), + act("code.cleanup", "Code Cleanup…"), SEP, act("code.commentLine", "Comment with Line Comment"), act("code.commentBlock", "Comment with Block Comment"), @@ -469,6 +522,7 @@ pub fn spec(os: Os) -> Vec { act("run.stop", "Stop"), SEP, act("run.chooseConfig", "Run…"), + act("runAnything.show", "Run Anything…"), act("run.editConfigs", "Edit Configurations…"), act("debug.attachToProcess", "Attach to Process…"), SEP, @@ -508,13 +562,20 @@ pub fn spec(os: Os) -> Vec { act("vcs.commit", "Commit…"), act("vcs.push", "Push…"), act("vcs.update", "Update Project…"), + act("vcs.pull", "Pull…"), act("vcs.fetch", "Fetch"), SEP, + act("vcs.merge", "Merge…"), + act("vcs.rebase", "Rebase…"), + SEP, act("vcs.branches", "Branches…"), act("vcs.newBranch", "New Branch…"), act("vcs.newTag", "New Tag…"), act("vcs.resetHead", "Reset HEAD…"), SEP, + act("vcs.newWorktree", "New Worktree…"), + act("vcs.worktrees", "Worktrees…"), + SEP, act("vcs.showDiff", "Show Diff"), act("vcs.rollback", "Rollback…"), act("vcs.annotate", "Annotate with Git Blame"), @@ -552,6 +613,10 @@ pub fn spec(os: Os) -> Vec { act("github.openInBrowser", "Open on GitHub"), ], ), + act("vcs.manageRemotes", "Manage Remotes…"), + act("vcs.clone", "Clone…"), + SEP, + act("vcs.operations", "VCS Operations Popup…"), ], )); @@ -573,14 +638,64 @@ pub fn spec(os: Os) -> Vec { window.extend([Entry::Native(Native::Minimize), Entry::Native(Native::Zoom), SEP]); } window.extend([ - act("window.new", "New Window"), - accel("window.next", "Next Project Window", "CmdOrCtrl+`"), - accel("window.previous", "Previous Project Window", "CmdOrCtrl+Shift+`"), + sub( + "toolWindowLayouts", + "Tool Window Layouts", + vec![ + act("layout.save", "Save Current Layout as…"), + act("layout.restoreDefault", "Restore Default Layout"), + SEP, + Entry::Dynamic(DYNAMIC_LAYOUTS), + SEP, + act("layout.rename", "Rename Layout…"), + act("layout.delete", "Delete Layout…"), + ], + ), SEP, - act("editor.splitRight", "Split Right"), - act("editor.splitDown", "Split Down"), - act("editor.unsplit", "Unsplit"), + sub( + "activeToolWindow", + "Active Tool Window", + vec![ + act("toolwindow.hideActive", "Hide Active Tool Window"), + act("toolwindow.maximize", "Maximize Tool Window"), + act("toolwindow.closeContent", "Close Active Tab"), + SEP, + act("toolwindow.jumpToLast", "Jump to Last Tool Window"), + act("toolwindow.focusEditor", "Focus Editor"), + ], + ), + sub( + "editorTabs", + "Editor Tabs", + vec![ + act("editor.nextTab", "Select Next Tab"), + act("editor.prevTab", "Select Previous Tab"), + SEP, + act("editor.pinTab", "Pin Tab"), + act("editor.closeTab", "Close Tab"), + act("editor.closeOtherTabs", "Close Other Tabs"), + act("editor.closeAllTabs", "Close All Tabs"), + act("editor.reopenClosedTab", "Reopen Closed Tab"), + SEP, + act("editor.splitRight", "Split Right"), + act("editor.splitDown", "Split Down"), + act("editor.unsplit", "Unsplit"), + act("editor.moveToOppositeGroup", "Move to Opposite Group"), + ], + ), + SEP, + act("window.new", "New Window"), ]); + // ⌘` is macOS's window cycling; elsewhere Ctrl+` is Quick Switch Scheme and the keymap's + // Ctrl+Alt+] / Ctrl+Alt+[ switch windows. + if mac { + window.extend([ + accel("window.next", "Next Project Window", "Cmd+`"), + accel("window.previous", "Previous Project Window", "Cmd+Shift+`"), + ]); + } else { + window.extend([act("window.next", "Next Project Window"), act("window.previous", "Previous Project Window")]); + } if mac { window.extend([SEP, Entry::Native(Native::BringAllToFront)]); } @@ -613,6 +728,13 @@ pub fn route(spec: &[Entry], id: &str) -> Option { if let Some(path) = id.strip_prefix(RECENT_PREFIX) { return Some(MenuRoute { action: OPEN_RECENT_ACTION.into(), arg: Some(path.into()) }); } + if let Some(rest) = id.strip_prefix(DYNAMIC_PREFIX) { + let (action, arg) = match rest.split_once('|') { + Some((action, arg)) => (action, Some(arg.to_owned())), + None => (rest, None), + }; + return (!action.is_empty()).then(|| MenuRoute { action: action.into(), arg }); + } action_ids(spec).into_iter().any(|a| a == id).then(|| MenuRoute { action: id.into(), arg: None }) } @@ -660,6 +782,28 @@ pub struct MenuConfig { pub labels: HashMap, /// Action id → display shortcut from the JS keymap, e.g. `⇧⌘O`. pub shortcuts: HashMap, + /// [`Entry::Dynamic`] slot → its items, already localised. + #[serde(default)] + pub dynamic: HashMap>, +} + +/// An item of a frontend-listed menu slot. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Type)] +#[serde(rename_all = "camelCase")] +pub struct DynamicItem { + pub action: String, + pub arg: Option, + pub label: String, +} + +impl DynamicItem { + /// Native menu item id, decoded again by [`route`]. + pub fn item_id(&self) -> String { + match &self.arg { + Some(arg) => format!("{DYNAMIC_PREFIX}{}|{arg}", self.action), + None => format!("{DYNAMIC_PREFIX}{}", self.action), + } + } } impl MenuConfig { @@ -667,6 +811,18 @@ impl MenuConfig { self.labels.get(&format!("menu.{id}")).cloned().unwrap_or_else(|| default.to_owned()) } + fn dynamic_items(&self, slot: &str) -> &[DynamicItem] { + self.dynamic.get(slot).map(Vec::as_slice).unwrap_or_default() + } + + /// A dynamic item's text: its label, plus the action's shortcut unless it carries an argument. + fn dynamic_text(&self, item: &DynamicItem) -> String { + match self.shortcuts.get(&item.action).filter(|s| item.arg.is_none() && !s.is_empty()) { + Some(shortcut) => format!("{}\t{shortcut}", item.label), + None => item.label.clone(), + } + } + /// Item text with the keymap shortcut appended after a tab (display only). pub fn item_text(&self, id: &str, default: &str, hint: Option<&str>) -> String { let label = self.label(id, default); @@ -680,6 +836,48 @@ impl MenuConfig { #[derive(Default)] pub struct MenuState { pub config: Mutex, + /// Action ids the focused window reports as disabled (no context / not registered). + pub disabled: Mutex>, +} + +/// The action an item id stands for, if it is an IDE action item. +fn item_action(id: &str) -> Option<&str> { + if id.starts_with(RECENT_PREFIX) || id == "recent.none" || id.starts_with("submenu:") { + return None; + } + Some(id.strip_prefix(DYNAMIC_PREFIX).map_or(id, |rest| rest.split_once('|').map_or(rest, |(a, _)| a))) +} + +/// Whether the item `id` is enabled given the disabled action ids. +pub fn item_enabled(id: &str, disabled: &HashSet) -> bool { + item_action(id).is_none_or(|a| !disabled.contains(a)) +} + +/// Updates the enabled state of the installed menu's items in place (no rebuild). +pub fn apply_disabled(app: &AppHandle, disabled: &HashSet) { + fn walk(items: Vec>, disabled: &HashSet) { + for item in items { + match item { + MenuItemKind::MenuItem(i) => { + let enabled = item_enabled(i.id().as_ref(), disabled); + if i.is_enabled().is_ok_and(|e| e != enabled) { + let _ = i.set_enabled(enabled); + } + } + MenuItemKind::Submenu(s) => { + if let Ok(children) = s.items() { + walk(children, disabled) + } + } + _ => {} + } + } + } + if let Some(menu) = app.menu() + && let Ok(items) = menu.items() + { + walk(items, disabled); + } } pub struct RecentItem { @@ -707,6 +905,7 @@ pub fn build( os: Os, config: &MenuConfig, recent: &[RecentItem], + disabled: &HashSet, ) -> tauri::Result> { let menu = Menu::new(app)?; let mut spec = spec(os); @@ -715,7 +914,7 @@ pub fn build( } for entry in spec { if let Entry::Submenu { id, label, children } = entry { - let submenu = build_submenu(app, config, recent, id, label, &children)?; + let submenu = build_submenu(app, config, recent, disabled, id, label, &children)?; #[cfg(target_os = "macos")] match id { "window" => submenu.set_as_windows_menu_for_nsapp()?, @@ -732,6 +931,7 @@ fn build_submenu( app: &AppHandle, config: &MenuConfig, recent: &[RecentItem], + disabled: &HashSet, id: &str, label: &str, children: &[Entry], @@ -740,15 +940,23 @@ fn build_submenu( for child in children { match child { Entry::Submenu { id, label, children } => { - submenu.append(&build_submenu(app, config, recent, id, label, children)?)? + submenu.append(&build_submenu(app, config, recent, disabled, id, label, children)?)? } Entry::Action { id, label, accel, hint } => { + let enabled = item_enabled(id, disabled); let item = match accel { - Some(a) => MenuItem::with_id(app, *id, config.label(id, label), true, Some(*a))?, - None => MenuItem::with_id(app, *id, config.item_text(id, label, *hint), true, None::<&str>)?, + Some(a) => MenuItem::with_id(app, *id, config.label(id, label), enabled, Some(*a))?, + None => MenuItem::with_id(app, *id, config.item_text(id, label, *hint), enabled, None::<&str>)?, }; submenu.append(&item)?; } + Entry::Dynamic(slot) => { + for d in config.dynamic_items(slot) { + let id = d.item_id(); + let enabled = item_enabled(&id, disabled); + submenu.append(&MenuItem::with_id(app, id, config.dynamic_text(d), enabled, None::<&str>)?)?; + } + } Entry::Native(n) => submenu.append(native(app, config, *n)?.as_ref())?, Entry::Separator => submenu.append(&PredefinedMenuItem::separator(app)?)?, Entry::RecentProjects => { @@ -814,15 +1022,21 @@ pub enum MenuNode { } /// The spec as [`MenuNode`]s; native role items (macOS only) are left out. -pub fn model(spec: &[Entry], recent: &[RecentItem]) -> Vec { +pub fn model(spec: &[Entry], recent: &[RecentItem], config: &MenuConfig) -> Vec { let mut out = Vec::new(); for e in spec { match e { Entry::Submenu { id, label, children } => out.push(MenuNode::Submenu { id: (*id).into(), label: (*label).into(), - children: model(children, recent), + children: model(children, recent, config), }), + Entry::Dynamic(slot) => out.extend(config.dynamic_items(slot).iter().map(|d| MenuNode::Action { + id: d.action.clone(), + label: d.label.clone(), + hint: None, + arg: d.arg.clone(), + })), Entry::Action { id, label, hint, .. } => out.push(MenuNode::Action { id: (*id).into(), label: (*label).into(), @@ -859,7 +1073,8 @@ pub fn install(app: &AppHandle, menu_state: &MenuState, recent: & let home = dirs::home_dir(); let items = recent_items(recent, home.as_deref()); let config = menu_state.config.lock().clone(); - match build(app, Os::current(), &config, &items).and_then(|m| app.set_menu(m)) { + let disabled = menu_state.disabled.lock().clone(); + match build(app, Os::current(), &config, &items, &disabled).and_then(|m| app.set_menu(m)) { Ok(_) => {} Err(e) => tracing::warn!("failed to build the app menu: {e}"), } @@ -1107,14 +1322,131 @@ mod tests { vec![Entry::Native(Native::Undo), act("file.save", "Save All"), SEP, Entry::RecentProjects], )]; let recent = [RecentItem { path: "/p/a".into(), label: "a — ~/a".into() }]; - let MenuNode::Submenu { children, .. } = &model(&spec, &recent)[0] else { panic!("not a submenu") }; + let MenuNode::Submenu { children, .. } = &model(&spec, &recent, &MenuConfig::default())[0] else { + panic!("not a submenu") + }; assert_eq!(children.len(), 3); assert!(matches!(&children[0], MenuNode::Action { id, arg: None, .. } if id == "file.save")); assert!( matches!(&children[2], MenuNode::Action { id, arg: Some(p), .. } if id == OPEN_RECENT_ACTION && p == "/p/a") ); - let MenuNode::Submenu { children, .. } = &model(&spec, &[])[0] else { panic!("not a submenu") }; + let MenuNode::Submenu { children, .. } = &model(&spec, &[], &MenuConfig::default())[0] else { + panic!("not a submenu") + }; assert!(matches!(&children[2], MenuNode::Placeholder { id, .. } if id == "recent.none")); } + + fn dynamic_item(action: &str, arg: Option<&str>, label: &str) -> DynamicItem { + DynamicItem { action: action.into(), arg: arg.map(Into::into), label: label.into() } + } + + #[test] + fn dynamic_items_route_with_their_argument() { + let spec = spec(Os::Mac); + let plain = dynamic_item("toolwindow.project", None, "Project"); + let named = dynamic_item("layout.restore", Some("Review | wide"), "Review | wide"); + assert_eq!(route(&spec, &plain.item_id()), Some(MenuRoute { action: "toolwindow.project".into(), arg: None })); + assert_eq!( + route(&spec, &named.item_id()), + Some(MenuRoute { action: "layout.restore".into(), arg: Some("Review | wide".into()) }) + ); + assert_eq!(route(&spec, "dyn:"), None); + } + + #[test] + fn tool_windows_and_layouts_are_listed_by_the_frontend() { + fn slots(entries: &[Entry], out: &mut Vec<&'static str>) { + for e in entries { + match e { + Entry::Submenu { children, .. } => slots(children, out), + Entry::Dynamic(slot) => out.push(slot), + _ => {} + } + } + } + for os in ALL_OS { + let spec = spec(os); + let mut found = Vec::new(); + slots(&spec, &mut found); + assert_eq!(found, [DYNAMIC_TOOL_WINDOWS, DYNAMIC_LAYOUTS], "{os:?}"); + assert!(!action_ids(&spec).iter().any(|id| { + id.starts_with("toolwindow.") + && *id != "toolwindow.hideAll" + && ![ + "toolwindow.hideActive", + "toolwindow.maximize", + "toolwindow.closeContent", + "toolwindow.jumpToLast", + "toolwindow.focusEditor", + ] + .contains(id) + })); + } + let mut config = MenuConfig::default(); + config.dynamic.insert( + DYNAMIC_TOOL_WINDOWS.into(), + vec![dynamic_item("toolwindow.project", None, "Project"), dynamic_item("toolwindow.git", None, "Git")], + ); + config.shortcuts.insert("toolwindow.project".into(), "⌘1".into()); + assert_eq!(config.dynamic_text(&config.dynamic[DYNAMIC_TOOL_WINDOWS][0]), "Project\t⌘1"); + assert_eq!(config.dynamic_text(&dynamic_item("toolwindow.project", Some("x"), "X")), "X"); + let spec = vec![sub("toolWindows", "Tool Windows", vec![Entry::Dynamic(DYNAMIC_TOOL_WINDOWS), SEP])]; + let MenuNode::Submenu { children, .. } = &model(&spec, &[], &config)[0] else { panic!("not a submenu") }; + assert!( + matches!(&children[1], MenuNode::Action { id, arg: None, label, .. } if id == "toolwindow.git" && label == "Git") + ); + let MenuNode::Submenu { children, .. } = &model(&spec, &[], &MenuConfig::default())[0] else { panic!() }; + assert_eq!(children, &[MenuNode::Separator]); + } + + #[test] + fn disabled_actions_disable_their_items_only() { + let disabled: HashSet = ["refactor.rename".to_owned(), "layout.restore".to_owned()].into(); + assert!(!item_enabled("refactor.rename", &disabled)); + assert!(item_enabled("refactor.inline", &disabled)); + assert!(!item_enabled("dyn:layout.restore|Review", &disabled)); + assert!(item_enabled("dyn:toolwindow.git", &disabled)); + assert!(item_enabled("recent:/p/refactor.rename", &disabled)); + assert!(item_enabled("submenu:refactor", &disabled)); + } + + #[test] + fn idea_menu_items_are_present() { + for os in ALL_OS { + let ids = action_ids(&spec(os)); + for required in [ + "file.newProject", + "file.renameProject", + "file.reloadAllFromDisk", + "file.exportSettings", + "file.importSettings", + "file.togglePowerSave", + "macro.startRecording", + "macro.stopRecording", + "macro.playBack", + "macro.playSaved", + "macro.edit", + "navigate.toggleBookmark", + "navigate.showBookmarks", + "code.quickDefinition", + "code.quickTypeDefinition", + "localHistory.recentChanges", + "editor.resetFontSize", + "diff.compareWithClipboard", + "view.quickSwitchScheme", + "code.cleanup", + "runAnything.show", + "layout.save", + "layout.restoreDefault", + "layout.rename", + "layout.delete", + "toolwindow.hideActive", + "editor.closeOtherTabs", + "toolbar.customize", + ] { + assert!(ids.contains(&required), "{required} missing on {os:?}"); + } + } + } } diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 7b4fc634..e0e436ce 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -2,7 +2,7 @@ use crate::events::TauriSink; use ide_core::{CancellationToken, IdeError, IdeEvent, IdeResult, RequestId, SharedSink}; use ide_dap::DebugManager; use ide_index::ProjectIndex; -use ide_language::{LanguageRegistry, Syntax, SyntaxCache}; +use ide_language::{FileTypes, LanguageRegistry, Syntax, SyntaxCache}; use ide_lsp::LspManager; use ide_run::ProcessManager; use ide_syntax::StructureService; @@ -51,6 +51,8 @@ pub struct Project { pub root: PathBuf, /// Window-scoped event sink, for commands that emit project events. pub sink: SharedSink, + /// The app's language definitions with this project's File Types applied. + pub languages: Arc, pub settings: Arc, pub documents: Arc, pub history: Arc, @@ -72,7 +74,9 @@ pub struct Project { pub db: Arc, /// HTTP Client responses (IDEA's `.idea/httpRequests`), in the app data dir. pub http_history: Arc, - _watcher: Watcher, + watcher: Watcher, + /// Watchers of the Project Structure content roots outside the project folder. + content_watchers: Mutex>, _scratch_watcher: Option, } @@ -214,10 +218,19 @@ impl AppState { let settings = SettingsStore::load(&self.config_dir, Some(&root)); let detected_roots = Arc::new(RwLock::new(ide_workspace::roots::detect(&root))); let excludes = exclude_globs(&settings.effective(), &detected_roots.read()); + let extra_roots = content_roots(&settings.effective(), &root); mark("roots"); - let languages = self.languages.clone(); - let documents = Arc::new(Documents::new(sink.clone(), Box::new(move |p: &Path| languages.detect(p, None)))); + // The project's own registry: File Types associations may be project-scoped. + let languages = Arc::new(self.languages.fork()); + languages.set_file_types(FileTypes::from_settings(&settings.effective().files, Some(&root))); + let documents = { + let languages = languages.clone(); + Arc::new(Documents::new( + sink.clone(), + Box::new(move |p: &Path, first: Option<&str>| languages.detect(p, first)), + )) + }; documents.set_file_defaults(ide_workspace::FileDefaults::from_settings(&settings.effective(), Some(&root))); let history = Arc::new(LocalHistory::open(&self.data_dir.join("history"), &root)?); { @@ -233,13 +246,14 @@ impl AppState { let index = ProjectIndex::open( &root, &self.data_dir.join("index").join(&project_key), + extra_roots.clone(), excludes.clone(), - self.languages.clone(), + languages.clone(), self.syntax(), sink.clone(), )?; mark("index"); - let lsp = LspManager::new(root.clone(), self.languages.clone(), sink.clone()); + let lsp = LspManager::new(root.clone(), languages.clone(), sink.clone()); let settings = Arc::new(settings); { let settings = settings.clone(); @@ -301,7 +315,7 @@ impl AppState { let history = history.clone(); Watcher::start( &root.clone(), - excludes, + excludes.clone(), sink.clone(), Box::new(move |batch| { let changed: Vec = batch.created.iter().chain(&batch.modified).cloned().collect(); @@ -314,6 +328,9 @@ impl AppState { if touched.into_iter().any(ide_workspace::roots::affects_roots) { let fresh = ide_workspace::roots::detect(&root); if *detected_roots.read() != fresh { + // Detected excluded folders (`target`, `build`) leave the index too. + let effective = settings.effective(); + index.set_scope(content_roots(&effective, &root), exclude_globs(&effective, &fresh)); *detected_roots.write() = fresh; sink.emit(IdeEvent::ProjectRootsChanged { root: root.clone().into() }); } @@ -356,6 +373,13 @@ impl AppState { }) }; + let content_watchers = extra_roots + .iter() + .filter_map(|r| { + let w = content_root_watcher(r, &excludes, &sink, &index, &documents, &history)?; + Some((r.clone(), w)) + }) + .collect(); mark("watcher"); let branch = repos.read().primary().and_then(|r| r.branch_info().ok()).and_then(|b| b.head); { @@ -366,6 +390,10 @@ impl AppState { } mark("git"); self.recent.touch(&root, branch)?; + let display_name = settings.effective().general.project_name; + if !display_name.trim().is_empty() { + self.recent.rename(&root, Some(display_name.trim()))?; + } mark("recent"); tracing::info!("opened {} in window {window} in {} ms {laps:?}", root.display(), started.elapsed().as_millis()); @@ -374,6 +402,7 @@ impl AppState { let project = Arc::new(Project { root, sink, + languages, settings, documents, history, @@ -387,7 +416,8 @@ impl AppState { cache_dir: self.data_dir.join("cache").join(&project_key), db, http_history, - _watcher: watcher, + watcher, + content_watchers: Mutex::new(content_watchers), _scratch_watcher: scratch_watcher, }); self.projects.write().insert(window.to_owned(), project.clone()); @@ -538,11 +568,15 @@ impl Project { } /// File masks (`ide_search::FileMask`) for Find in Files and inspections: the - /// excluded roots and the ignored names (files, and everything under such folders). + /// excluded roots, the ignored names and the "Exclude files" patterns (files, + /// and everything under such folders). pub fn search_excludes(&self) -> Vec { let mut out: Vec = self.roots().excluded_globs().iter().map(|d| format!("{}/**", d.trim_start_matches('/'))).collect(); - for name in &self.settings.effective().files.ignored_names { + let settings = self.settings.effective(); + let names = + settings.files.ignored_names.iter().cloned().chain(settings.project_structure.exclude_file_patterns()); + for name in names { let name = name.trim(); if !name.is_empty() && !name.contains(['/', ',']) { out.push(name.to_owned()); @@ -557,6 +591,45 @@ impl Project { exclude_globs(&self.settings.effective(), &self.detected_roots.read()) } + /// Project Structure content roots besides the project folder (existing directories). + pub fn content_roots(&self) -> Vec { + content_roots(&self.settings.effective(), &self.root) + } + + /// Applies Settings › File Types; open documents whose language changed are + /// re-detected (language servers and syntax trees follow) and the index + /// re-parses the files whose language changed. + pub fn apply_file_types(&self, settings: &ide_core::settings::Settings) { + let before = Arc::new(self.languages.snapshot()); + if self.languages.set_file_types(FileTypes::from_settings(&settings.files, Some(&self.root))) { + self.documents.redetect_languages(); + self.index.languages_changed(before); + } + } + + /// Applies changed content roots and exclude settings to the index (which + /// rescans in the background) and the file watchers, without reopening the project. + pub fn refresh_index_scope(&self) { + let (roots, excludes) = (self.content_roots(), self.exclude_globs()); + self.index.set_scope(roots.clone(), excludes.clone()); + self.watcher.set_excludes(&excludes); + let mut watchers = self.content_watchers.lock(); + watchers.retain(|(p, _)| roots.contains(p)); + for (_, w) in watchers.iter() { + w.set_excludes(&excludes); + } + for r in roots { + if watchers.iter().any(|(p, _)| *p == r) { + continue; + } + if let Some(w) = + content_root_watcher(&r, &excludes, &self.sink, &self.index, &self.documents, &self.history) + { + watchers.push((r, w)); + } + } + } + /// Current text of `path`: the open buffer when available, else the file on disk. pub fn text_of(&self, path: &Path) -> IdeResult { match self.documents.text_of(path) { @@ -624,6 +697,44 @@ pub fn history_retention(settings: &ide_core::settings::Settings) -> ide_workspa } } +/// Project Structure content roots outside `root` (existing absolute directories). +fn content_roots(settings: &ide_core::settings::Settings, root: &Path) -> Vec { + let mut out: Vec = Vec::new(); + for r in &settings.project_structure.content_roots { + let p = PathBuf::from(r.trim()); + if p.is_absolute() && p.is_dir() && !p.starts_with(root) && !out.contains(&p) { + out.push(p); + } + } + out +} + +/// Watches a content root outside the project: the index, open documents and +/// Local History follow its changes like the project folder's. +fn content_root_watcher( + dir: &Path, + excludes: &[String], + sink: &SharedSink, + index: &Arc, + documents: &Arc, + history: &Arc, +) -> Option { + let (index, documents, history) = (index.clone(), documents.clone(), history.clone()); + Watcher::start( + dir, + excludes.to_vec(), + sink.clone(), + Box::new(move |batch| { + let changed: Vec = batch.created.iter().chain(&batch.modified).cloned().collect(); + index.update(&changed, &batch.removed); + documents.on_disk_changed(&changed); + record_external_changes(&history, &changed, &batch.removed); + }), + ) + .map_err(|e| tracing::warn!("content root watcher {}: {e}", dir.display())) + .ok() +} + fn exclude_globs(settings: &ide_core::settings::Settings, detected: &[SourceFolder]) -> Vec { let mut globs = settings.file_excludes(); globs.extend(ProjectRoots::compute(detected.to_vec(), &settings.project_structure).excluded_globs()); diff --git a/src/bindings.ts b/src/bindings.ts index 5ede6f18..7b0a5b7c 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -33,6 +33,18 @@ export const commands = { * re-reads its effective settings. */ settingsSet: (scope: SettingsScope, key: string, value: string) => typedError(__TAURI_INVOKE("settings_set", { scope, key, value })), + /** + * File › Manage IDE Settings › Export Settings…: the global settings plus the + * frontend's IDE-level `storage` (live / file templates, macros) to `path`. + */ + settingsExport: (path: string, storage: { [key in string]: string }) => typedError(__TAURI_INVOKE("settings_export", { path, storage })), + /** Import Settings…: replaces the global settings with the file's; every window re-reads them. */ + settingsImport: (path: string) => typedError(__TAURI_INVOKE("settings_import", { path })), + /** + * File › Rename Project…: the project's display name (title bar, recent + * projects); an empty `name` restores the folder name. Stored in the project's settings. + */ + projectRename: (name: string) => typedError(__TAURI_INVOKE("project_rename", { name })), /** * Effective source / test / resource / excluded roots of the window's project: * auto-detected from build files, overridden by Settings › Project Structure. @@ -91,7 +103,8 @@ export const commands = { docSetEncoding: (doc: DocId, encoding: string, reload: boolean) => typedError(__TAURI_INVOKE("doc_set_encoding", { doc, encoding, reload })), docSetLineEnding: (doc: DocId, lineEnding: LineEnding) => typedError(__TAURI_INVOKE("doc_set_line_ending", { doc, lineEnding })), /** - * Structure view: LSP document symbols when available, tree-sitter otherwise. + * Structure view: LSP document symbols when available (with tree-sitter + * visibility), tree-sitter otherwise. * Tree-sitter answers come from the per-document tree cache (incremental * re-parse); large documents get no syntax structure. */ @@ -198,6 +211,13 @@ export const commands = { pathCompletions: (from: IpcPath, prefix: string) => typedError(__TAURI_INVOKE("path_completions", { from, prefix })), /** Paths in an HTML document or stylesheet that name no existing file. */ webUnresolvedReferences: (doc: DocId) => typedError(__TAURI_INVOKE("web_unresolved_references", { doc })), + /** + * Misspelt words in the comments, string literals and declared names of + * `doc` (all of its text for prose). Cancel with `cancel_request(request)`. + */ + spellCheck: (doc: DocId, request: RequestId) => typedError(__TAURI_INVOKE("spell_check", { doc, request })), + /** Replacements for a misspelt `word`, best first (Typo › Change to…). */ + spellSuggest: (word: string) => typedError(__TAURI_INVOKE("spell_suggest", { word })), /** * Find in Files. Results stream through `on_event`; ends with `Done`. * Cancel with `cancel_request(request)`. @@ -218,6 +238,8 @@ export const commands = { /** Validates a query (e.g. regex syntax) while the user types. */ searchValidate: (query: SearchQuery) => typedError(__TAURI_INVOKE("search_validate", { query })), lspCompletion: (request: RequestId, doc: DocId, pos: Pos, trigger: string | null) => typedError(__TAURI_INVOKE("lsp_completion", { request, doc, pos, trigger })), + /** Inline (ghost text) suggestions at `pos`; `automatic` when triggered by typing. */ + lspInlineCompletion: (request: RequestId, doc: DocId, pos: Pos, automatic: boolean) => typedError(__TAURI_INVOKE("lsp_inline_completion", { request, doc, pos, automatic })), lspResolveCompletion: (doc: DocId, item: CompletionItem) => typedError(__TAURI_INVOKE("lsp_resolve_completion", { doc, item })), lspHover: (request: RequestId, doc: DocId, pos: Pos) => typedError<{ contents: Markup, @@ -328,6 +350,8 @@ export const commands = { vcsStageHunks: (path: string, hunks: number[]) => typedError(__TAURI_INVOKE("vcs_stage_hunks", { path, hunks })), /** Clones a repository (Get from VCS). Works without an open project. */ vcsClone: (request: RequestId, url: string, dest: string, onOutput: Channel) => typedError(__TAURI_INVOKE("vcs_clone", { request, url, dest, onOutput })), + /** `git init` in `dir` (File › New Project… "Create Git repository"). */ + vcsInit: (dir: string) => typedError(__TAURI_INVOKE("vcs_init", { dir })), /** * Settings › Git › Test: runs `git --version` on `path`, or on the auto-detected * executable when `path` is empty. @@ -344,6 +368,15 @@ export const commands = { vcsOperation: (repo: string | null, request: RequestId, op: GitOperation, onOutput: Channel) => typedError(__TAURI_INVOKE("vcs_operation", { repo, request, op, onOutput })), vcsCreateTag: (repo: string | null, name: string, commit: string, message: string | null, force: boolean) => typedError(__TAURI_INVOKE("vcs_create_tag", { repo, name, commit, message, force })), vcsTags: (repo: string | null) => typedError(__TAURI_INVOKE("vcs_tags", { repo })), + /** Merge dialog: merges `branches` into the current branch. */ + vcsMergeWith: (repo: string | null, branches: string[], options: MergeOptions) => typedError(__TAURI_INVOKE("vcs_merge_with", { repo, branches, options })), + /** Rebase dialog: rebases the current branch onto `upstream` (or `--onto`). */ + vcsRebaseWith: (repo: string | null, upstream: string, options: RebaseOptions) => typedError(__TAURI_INVOKE("vcs_rebase_with", { repo, upstream, options })), + vcsRemotes: (repo: string | null) => typedError(__TAURI_INVOKE("vcs_remotes", { repo })), + vcsEditRemote: (repo: string | null, edit: RemoteEdit) => typedError(__TAURI_INVOKE("vcs_edit_remote", { repo, edit })), + vcsWorktrees: (repo: string | null) => typedError(__TAURI_INVOKE("vcs_worktrees", { repo })), + vcsAddWorktree: (repo: string | null, path: string, commitish: string, newBranch: string | null) => typedError(__TAURI_INVOKE("vcs_add_worktree", { repo, path, commitish, newBranch })), + vcsRemoveWorktree: (repo: string | null, path: string, force: boolean) => typedError(__TAURI_INVOKE("vcs_remove_worktree", { repo, path, force })), /** Compare with Local: tracked files that differ between `rev` and the working tree. */ vcsDiffWithWorktree: (repo: string | null, rev: string) => typedError(__TAURI_INVOKE("vcs_diff_with_worktree", { repo, rev })), /** Files that differ between two revisions (the "N files updated" of Update Project). */ @@ -382,11 +415,18 @@ export const commands = { /** Detected configs followed by user-defined ones (user ids win on conflict). */ runConfigs: () => typedError(__TAURI_INVOKE("run_configs")), runSaveConfigs: (configs: RunConfig_Deserialize[]) => typedError(__TAURI_INVOKE("run_save_configs", { configs })), + /** Configuration templates: per-type defaults applied to new configurations. */ + runTemplates: () => typedError(__TAURI_INVOKE("run_templates")), + runSaveTemplates: (templates: RunConfig_Deserialize[]) => typedError(__TAURI_INVOKE("run_save_templates", { templates })), + /** The environment runs inherit when "Include system environment variables" is on. */ + runSystemEnv: () => __TAURI_INVOKE<{ [key in string]: string }>("run_system_env"), runStart: (config: RunConfig_Deserialize, onOutput: Channel) => typedError(__TAURI_INVOKE("run_start", { config, onOutput })), runStop: (id: RunId) => typedError(__TAURI_INVOKE("run_stop", { id })), runStdin: (id: RunId, data: string) => typedError(__TAURI_INVOKE("run_stdin", { id, data })), runList: () => __TAURI_INVOKE("run_list"), runCloseStdin: (id: RunId) => typedError(__TAURI_INVOKE("run_close_stdin", { id })), + /** Terminal size of a run started with "Emulate terminal in output console". */ + runResize: (id: RunId, cols: number, rows: number) => typedError(__TAURI_INVOKE("run_resize", { id, cols, rows })), /** * Results from the JUnit XML reports (Surefire, Failsafe, Gradle) of the module at * `module` (relative to the project root) written since `since_ms` (Unix epoch, ms). @@ -449,6 +489,11 @@ export const commands = { sdkDetect: (kind: SdkKind) => typedError(__TAURI_INVOKE("sdk_detect", { kind })), /** The interpreter the project uses: the configured one, else its virtualenv. */ sdkPythonEffective: () => typedError(__TAURI_INVOKE("sdk_python_effective")), + /** + * SDK / library source folders shown under "External Libraries" in the Project view, + * with the Maven / Gradle dependency jars of the build model. + */ + sdkProjectLibraries: () => typedError(__TAURI_INVOKE("sdk_project_libraries")), /** Looks up `names` (bare command names): `node_modules/.bin` above `near`, then the search path. */ sdkLocateTools: (names: string[], near: string | null) => typedError(__TAURI_INVOKE("sdk_locate_tools", { names, near })), /** `base -m venv location`; output lines stream through `on_output`. */ @@ -494,6 +539,11 @@ export const commands = { menuRefresh: () => __TAURI_INVOKE("menu_refresh"), /** The menu bar for a title bar drawn by the frontend (Windows). */ menuModel: () => __TAURI_INVOKE("menu_model"), + /** + * Action ids the focused window cannot run now: their native menu items are + * disabled in place (no rebuild). + */ + menuSetDisabled: (ids: string[]) => __TAURI_INVOKE("menu_set_disabled", { ids }), appLogLocation: () => typedError(__TAURI_INVOKE("app_log_location")), appBuildInfo: () => __TAURI_INVOKE("app_build_info"), appCheckUpdate: () => typedError(__TAURI_INVOKE("app_check_update")), @@ -541,6 +591,11 @@ export const commands = { coveragePrepare: (config: RunConfig_Deserialize) => typedError(__TAURI_INVOKE("coverage_prepare", { config })), /** Reads the report a coverage run left behind. */ coverageCollect: (report: CoverageReportSpec) => typedError(__TAURI_INVOKE("coverage_collect", { report })), + /** + * Reads a report collected elsewhere (CI): lcov, a Go cover profile or JaCoCo XML, + * with paths mapped into this project. + */ + coverageImport: (path: IpcPath) => typedError(__TAURI_INVOKE("coverage_import", { path })), /** The browsers installed on this machine. */ webBrowsers: () => typedError(__TAURI_INVOKE("web_browsers")), /** The built-in server's URL for `path` (started on first use). */ @@ -607,19 +662,32 @@ export const commands = { */ structuralSearch: (request: RequestId, scope: SearchScope, query: StructuralQuery, onEvent: Channel) => typedError(__TAURI_INVOKE("structural_search", { request, scope, query, onEvent })), /** Availability plus containers, images, volumes and networks. */ - dockerSnapshot: () => typedError(__TAURI_INVOKE("docker_snapshot")), - dockerContainerOp: (ids: string[], op: ContainerOp) => typedError(__TAURI_INVOKE("docker_container_op", { ids, op })), - dockerRemove: (object: DockerObject, ids: string[], force: boolean) => typedError(__TAURI_INVOKE("docker_remove", { object, ids, force })), + dockerSnapshot: (endpoint: DockerEndpoint) => typedError(__TAURI_INVOKE("docker_snapshot", { endpoint })), + dockerContainerOp: (endpoint: DockerEndpoint, ids: string[], op: ContainerOp) => typedError(__TAURI_INVOKE("docker_container_op", { endpoint, ids, op })), + dockerRemove: (endpoint: DockerEndpoint, object: DockerObject, ids: string[], force: boolean) => typedError(__TAURI_INVOKE("docker_remove", { endpoint, object, ids, force })), /** Pretty-printed `docker inspect` JSON. */ - dockerInspect: (object: DockerObject, id: string) => typedError(__TAURI_INVOKE("docker_inspect", { object, id })), + dockerInspect: (endpoint: DockerEndpoint, object: DockerObject, id: string) => typedError(__TAURI_INVOKE("docker_inspect", { endpoint, object, id })), /** Streams a container's log lines until it stops or the request is cancelled. */ - dockerLogs: (request: RequestId, id: string, tail: number, follow: boolean, timestamps: boolean, onLine: Channel) => typedError(__TAURI_INVOKE("docker_logs", { request, id, tail, follow, timestamps, onLine })), + dockerLogs: (request: RequestId, endpoint: DockerEndpoint, id: string, tail: number, follow: boolean, timestamps: boolean, onLine: Channel) => typedError(__TAURI_INVOKE("docker_logs", { request, endpoint, id, tail, follow, timestamps, onLine })), /** Follows `docker events` until the daemon goes away or the request is cancelled. */ - dockerEvents: (request: RequestId, onEvent: Channel) => typedError(__TAURI_INVOKE("docker_events", { request, onEvent })), - dockerPull: (request: RequestId, reference: string, onOutput: Channel) => typedError(__TAURI_INVOKE("docker_pull", { request, reference, onOutput })), - dockerPrune: (request: RequestId, target: PruneTarget, onOutput: Channel) => typedError(__TAURI_INVOKE("docker_prune", { request, target, onOutput })), + dockerEvents: (request: RequestId, endpoint: DockerEndpoint, onEvent: Channel) => typedError(__TAURI_INVOKE("docker_events", { request, endpoint, onEvent })), + /** `docker pull`, logging in first when a saved registry with a password serves the image. */ + dockerPull: (request: RequestId, endpoint: DockerEndpoint, reference: string, onOutput: Channel) => typedError(__TAURI_INVOKE("docker_pull", { request, endpoint, reference, onOutput })), + dockerPrune: (request: RequestId, endpoint: DockerEndpoint, target: PruneTarget, onOutput: Channel) => typedError(__TAURI_INVOKE("docker_prune", { request, endpoint, target, onOutput })), /** `docker compose` start / stop / restart / down of a project found through labels. */ - dockerComposeOp: (request: RequestId, project: string, files: string[], workingDir: string | null, op: ComposeOp, onOutput: Channel) => typedError(__TAURI_INVOKE("docker_compose_op", { request, project, files, workingDir, op, onOutput })), + dockerComposeOp: (request: RequestId, endpoint: DockerEndpoint, project: string, files: string[], workingDir: string | null, op: ComposeOp, onOutput: Channel) => typedError(__TAURI_INVOKE("docker_compose_op", { request, endpoint, project, files, workingDir, op, onOutput })), + /** Docker connections and registries (app-wide). */ + dockerSettings: () => typedError(__TAURI_INVOKE("docker_settings")), + dockerSaveSettings: (settings: DockerSettings) => typedError(__TAURI_INVOKE("docker_save_settings", { settings })), + /** Saves (`None`: forgets) a registry's password. */ + dockerSetRegistryPassword: (id: string, password: string | null) => typedError(__TAURI_INVOKE("docker_set_registry_password", { id, password })), + /** Test login to `registry` (`password`, else the saved one) through `endpoint`'s daemon. */ + dockerRegistryLogin: (endpoint: DockerEndpoint, registry: DockerRegistry, password: string | null) => typedError(__TAURI_INVOKE("docker_registry_login", { endpoint, registry, password })), + /** + * Environment for a Docker run configuration's command line: selects `endpoint`'s + * daemon and puts the docker executable on `PATH`. + */ + dockerRunEnv: (endpoint: DockerEndpoint) => typedError<{ [key in string]: string }, IdeError>(__TAURI_INVOKE("docker_run_env", { endpoint })), ghAvailability: () => typedError(__TAURI_INVOKE("gh_availability")), ghPrList: (query: PrListQuery) => typedError(__TAURI_INVOKE("gh_pr_list", { query })), ghPrView: (number: number) => typedError(__TAURI_INVOKE("gh_pr_view", { number })), @@ -778,6 +846,8 @@ export type AppearanceSettings = { * (status bar context menu); widgets not listed use their default. */ statusBarWidgets?: { [key in string]: boolean }, + /** Customize Main Toolbar; `None` = the default toolbar. */ + mainToolbar?: MainToolbarSettings | null, }; export type ApplyOutcome = { @@ -808,6 +878,16 @@ export type AutoImportSettings = { optimizeOnTheFly?: boolean, }; +/** A step run before a configuration launches; a failing step aborts the launch. */ +export type BeforeLaunchTask = { + /** + * `run` (another configuration, `target` = its id) | `build` | `tool` (external tool id) | + * `command` (a shell command line) | `toolWindow` (tool window id). + */ + kind: string, + target?: string, +}; + export type BlameLine = { commit: string, author: string, @@ -1095,6 +1175,14 @@ export type CodeStyleSettings_Deserialize = { /** Enable EditorConfig support. */ editorconfig?: boolean, languages?: { [key in string]: LanguageCodeStyle_Deserialize }, + /** Formatter › Do not format: globs (`*.min.js`, `generated/**`) Reformat Code skips. */ + doNotFormat?: string[], + /** Formatter › Turn formatter on/off with markers in code comments. */ + formatterTagsEnabled?: boolean, + formatterOffTag?: string, + formatterOnTag?: string, + /** The markers are regular expressions. */ + formatterTagsRegexp?: boolean, }; /** Editor › Code Style (General tab + per-language overrides). */ @@ -1108,6 +1196,14 @@ export type CodeStyleSettings_Serialize = { /** Enable EditorConfig support. */ editorconfig: boolean, languages: { [key in string]: LanguageCodeStyle_Serialize }, + /** Formatter › Do not format: globs (`*.min.js`, `generated/**`) Reformat Code skips. */ + doNotFormat: string[], + /** Formatter › Turn formatter on/off with markers in code comments. */ + formatterTagsEnabled: boolean, + formatterOffTag: string, + formatterOnTag: string, + /** The markers are regular expressions. */ + formatterTagsRegexp: boolean, }; export type Command = { @@ -1132,6 +1228,8 @@ export type Commit = { summary: string, message: string, refs: string[], + /** Committer time (Log › View Options › Commit Timestamp). */ + commitTimeMs?: number | null, }; /** Commit Options (IDEA's commit options popup). */ @@ -1209,6 +1307,12 @@ export type CompletionSettings = { autoPopup?: boolean, /** Insert the selected suggestion by pressing space, dot, or other context-dependent keys. */ insertOnSpecialKeys?: boolean, + /** Insert a sole suggestion of explicit (⌃Space) completion right away. */ + autoInsertSingle?: boolean, + /** Sort suggestions by name instead of relevance. */ + sortAlphabetically?: boolean, + /** Add `()` after function and method suggestions. */ + insertParentheses?: boolean, showDocumentation?: boolean, documentationDelay?: number, parameterInfo?: boolean, @@ -1272,6 +1376,12 @@ export type CreatePullRequest = { push: boolean, }; +/** A named profile made with Copy. */ +export type CustomInspectionProfile = { + name?: string, + overrides?: { [key in string]: InspectionOverride }, +}; + export type CustomKeymap = { name?: string, /** Bundled preset the keymap derives from, e.g. `intellij-macos`. */ @@ -1317,6 +1427,20 @@ export type DataSource = { readOnly?: boolean, /** SSH tunnel settings (`None`: never configured; kept but unused when not `enabled`). */ ssh?: DbSsh | null, + /** Wire-compatible product preset (`cockroachdb`, `tidb`…; empty: the driver itself). */ + vendor?: string, + color?: DbColor | null, + comment?: string, + /** Connect with [`DataSource::url`] instead of the host / port / database fields. */ + urlOnly?: boolean, + url?: string, + /** PEM files; `~/` is the home directory. */ + sslCaFile?: string, + sslCertFile?: string, + sslKeyFile?: string, + options?: DbOptions, + introspection?: DbIntrospection, + properties?: DbProperty[], }; /** System Settings › Date Formats (Git log, Local History, blame). */ @@ -1336,6 +1460,9 @@ export type DbCellValue = { value: string | null, }; +/** IDEA's data source colours (tree node and console tab tint), e.g. red for production. */ +export type DbColor = "red" | "orange" | "yellow" | "green" | "blue" | "violet"; + export type DbColumnInfo = { name: string, dataType: string, @@ -1394,6 +1521,41 @@ export type DbIndexInfo = { columns: string[], }; +/** Schemas tab: the databases / schemas the tree shows. Names may use `*` and `?`. */ +export type DbIntrospection = { + allDatabases?: boolean, + databases?: string[], + allSchemas?: boolean, + schemas?: string[], + /** `pg_catalog`, `information_schema`, `mysql`…, and PostgreSQL template databases. */ + showSystem?: boolean, +}; + +/** Options tab: session settings applied to every connection. */ +export type DbOptions = { + transaction?: DbTxMode, + /** PostgreSQL `search_path` (unqualified names resolve there). */ + defaultSchema?: string, + /** Session time zone (`TimeZone` / `time_zone`); empty keeps the default. */ + timeZone?: string, + /** Statements run on every new connection. */ + startupScript?: string, + /** Run `SELECT 1` this often while connected. */ + keepAliveSecs?: number | null, + /** Disconnect after this long without statements (enforced by the UI, which sees the activity). */ + autoDisconnectSecs?: number | null, +}; + +/** + * Advanced tab: a driver property. PostgreSQL: run-time parameters (and + * `application_name`); MySQL: session variables (and `charset`, `collation`); + * SQLite: pragmas. See [`crate::conn`]. + */ +export type DbProperty = { + name?: string, + value?: string, +}; + export type DbQueryResult = { statement: string, columns: DbResultColumn[], @@ -1443,7 +1605,11 @@ export type DbSshAuth = "password" | /** `~/.ssh/config` (HostName, User, Port, IdentityFile, ProxyJump) and the SSH agent. */ "openSsh"; -export type DbSslMode = "disable" | "prefer" | "require"; +export type DbSslMode = "disable" | "prefer" | "require" | +/** Require, and check the server certificate against the CA file. */ +"verifyCa" | +/** [`DbSslMode::VerifyCa`], and check that the certificate names the host. */ +"verifyFull"; export type DbStatementOutcome = { statement: SqlStatement, @@ -1470,6 +1636,9 @@ export type DbTablePage = { offset: number | null, }; +/** Transaction control of the data source's consoles. */ +export type DbTxMode = "auto" | "manual"; + /** What a session's adapter supports, for enabling actions. */ export type DebugCapabilities = { restartFrame: boolean, @@ -1534,6 +1703,11 @@ export type Diagnostic = { code: string | null, unnecessary?: boolean, deprecated?: boolean, + /** + * Language server (or tool) that reported it, e.g. `basedpyright`; `source` + * is what the producer calls itself. Inspection profiles key on this. + */ + server?: string | null, }; /** Changes under a folder (Show History on a directory). */ @@ -1563,6 +1737,12 @@ context: string | null } | /** The CLI is there but cannot reach a daemon (not started, wrong context…). */ { kind: "daemonDown"; message: string; context: string | null }; +export type DockerConnection = { + id: string, + name: string, + endpoint: DockerEndpoint, +}; + export type DockerContainer = { id: string, name: string, @@ -1583,6 +1763,20 @@ export type DockerContainer = { composeFiles: string[], }; +/** Which daemon a docker command talks to. */ +export type DockerEndpoint = +/** Whatever the CLI is configured for (`DOCKER_HOST`, the current context). */ +{ kind: "default" } | +/** A local daemon socket, e.g. `/var/run/docker.sock` (or a `npipe://` URL on Windows). */ +{ kind: "socket"; path: string } | +/** + * A remote daemon, e.g. `tcp://build:2376` or `ssh://me@build`; with TLS when + * `tls_cert_dir` holds `ca.pem`, `cert.pem` and `key.pem`. + */ +{ kind: "tcp"; host: string; tlsCertDir: string | null } | +/** A named `docker context`. */ +{ kind: "context"; name: string }; + /** One relevant `docker events` entry. */ export type DockerEvent = { /** `container`, `image`, `volume`, `network`. */ @@ -1617,6 +1811,20 @@ export type DockerNetwork = { export type DockerObject = "container" | "image" | "volume" | "network"; +/** A registry to log in to (Docker Hub, GHCR, a private one). The password is stored separately. */ +export type DockerRegistry = { + id: string, + name: string, + /** `docker.io`, `ghcr.io`, `registry.example.com:5000`. */ + address: string, + username?: string, +}; + +export type DockerSettings = { + connections?: DockerConnection[], + registries?: DockerRegistry[], +}; + /** Everything the Services tree shows, fetched in one go. */ export type DockerSnapshot = { availability: DockerAvailability, @@ -1628,6 +1836,8 @@ export type DockerSnapshot = { networks: DockerNetwork[], /** Lists that failed although the daemon answered (shown, not fatal). */ errors: string[], + /** Global CLI options selecting the connection's daemon (for `docker exec` in a terminal). */ + globalArgs?: string[], }; export type DockerVolume = { @@ -1705,6 +1915,13 @@ export type DuplicatesReport = { elapsedMs: number, }; +/** An item of a frontend-listed menu slot. */ +export type DynamicItem = { + action: string, + arg: string | null, + label: string, +}; + /** Editor › General › Appearance. */ export type EditorAppearanceSettings = { caretBlinking?: boolean, @@ -1795,6 +2012,7 @@ export type EditorSettings_Deserialize = { smartKeys?: SmartKeysSettings, codeEditing?: CodeEditingSettings, inlay?: InlayHintsSettings, + inlineCompletion?: InlineCompletionSettings, /** Gutter icon groups turned off (`run`, `breakpoints`, `bookmarks`, `vcs`, `blame`). */ gutterIconsDisabled?: string[], showGutterIcons?: boolean, @@ -1830,6 +2048,7 @@ export type EditorSettings_Serialize = { smartKeys: SmartKeysSettings, codeEditing: CodeEditingSettings, inlay: InlayHintsSettings, + inlineCompletion: InlineCompletionSettings, /** Gutter icon groups turned off (`run`, `breakpoints`, `bookmarks`, `vcs`, `blame`). */ gutterIconsDisabled: string[], showGutterIcons: boolean, @@ -1839,13 +2058,22 @@ export type EditorSettings_Serialize = { /** Editor › General › Editor Tabs. */ export type EditorTabsSettings = { - /** `top` | `none`. */ + /** `top` | `bottom` | `none`. */ placement?: string, multipleRows?: boolean, + /** One row that doesn't fit: `scroll` the tabs panel | `squeeze` the tabs. */ + oneRowOverflow?: string, + pinnedSeparateRow?: boolean, + showFileIcon?: boolean, showExtension?: boolean, + showDirectoryForNonUnique?: boolean, /** Mark modified tabs with an asterisk. */ markModified?: boolean, + showFullPathOnHover?: boolean, showCloseButton?: boolean, + /** `right` | `left` (with `show_close_button`). */ + closeButtonPosition?: string, + sortAlphabetically?: boolean, previewTab?: boolean, openAtEnd?: boolean, tabLimit?: number, @@ -2026,6 +2254,10 @@ export type FileSettings = { localHistoryDays?: number, /** Upper bound for a project's Local History in MB; the oldest revisions go first. */ localHistoryMaxMb?: number, + /** File Types › Recognized File Types: the user's changes, by language id. */ + fileTypes?: { [key in string]: FileTypeAssociation }, + /** Override File Type: file (project-relative and `/`-separated, or absolute) → language id. */ + fileTypeOverrides?: { [key in string]: string }, }; export type FileStatus = { @@ -2038,6 +2270,16 @@ export type FileStatus = { renamedFrom: string | null, }; +/** One language's file name and hashbang patterns on top of its definition. */ +export type FileTypeAssociation = { + /** Added file name patterns: `*.foo`, `Jenkinsfile`, `Dockerfile.*` (`*` and `?` wildcards). */ + patterns?: string[], + /** Patterns of the definition (`*.ext`, file names) the user removed. */ + removedPatterns?: string[], + /** Interpreters run by a `#!` first line, e.g. `node` for `#!/usr/bin/env node`. */ + hashbangs?: string[], +}; + export type FoldKind = "region" | "comment" | "imports"; export type FoldRange = { @@ -2091,6 +2333,13 @@ export type GeneralSettings = { saveOnIdle?: boolean, idleSeconds?: number, dateFormat?: DateFormatSettings, + /** + * File › Power Save Mode: no automatic popups (completion, parameter info, + * quick documentation) or background highlighting (usages, code vision). + */ + powerSaveMode?: boolean, + /** File › Rename Project… (project layer): the name shown for the project; empty = its folder name. */ + projectName?: string, }; /** Whether pull requests can be shown for the project, and why not. */ @@ -2332,6 +2581,8 @@ export type IdeEvent = { type: "docPatched"; doc: DocId; baseVersion: number; version: number; changes: TextChange[] } | /** A document's dirty flag changed (saved / modified externally). */ { type: "docDirty"; doc: DocId; dirty: boolean } | +/** An open document's language changed (Settings › File Types, Override File Type). */ +{ type: "docLanguage"; doc: DocId; language: LanguageId } | /** Full replacement of diagnostics for one file from one producer. */ { type: "diagnostics"; path: IpcPath; source: string; items: Diagnostic[] } | /** Progress of a background task; `fraction` is `None` when indeterminate. */ @@ -2411,6 +2662,8 @@ export type InlayHintsSettings = { disabledLanguages?: string[], /** Code vision: usages above declarations. */ codeVisionUsages?: boolean, + /** Code vision: last author of a declaration (VCS blame) beside its usages. */ + codeVisionAuthor?: boolean, }; export type InlayKind = "type" | "parameter" | "other"; @@ -2423,6 +2676,31 @@ export type InlayLabelPart = { location: Location | null, }; +/** One `textDocument/inlineCompletion` suggestion (ghost text at the caret). */ +export type InlineCompletionItem = { + /** Text that replaces `range`; snippets arrive as their plain text. */ + insertText: string, + /** Replaced range; `None` inserts at the caret. */ + range: Range | null, + /** What the typed text is matched against while the suggestion stays shown (default: `insert_text`). */ + filterText: string | null, + /** Run after the suggestion is accepted. */ + command: Command | null, + server: ServerId, +}; + +/** + * Editor › General › Inline Completion: ghost text from language servers that + * support `textDocument/inlineCompletion` (LSP 3.18). + */ +export type InlineCompletionSettings = { + enabled?: boolean, + /** Ask only once typing has paused for `pause_ms`, instead of right after each change. */ + onlyOnPause?: boolean, + pauseMs?: number, + disabledLanguages?: string[], +}; + export type InspectEvent = /** Files about to be inspected. */ { type: "started"; total: number } | @@ -2447,6 +2725,33 @@ export type InspectedFile = { diagnostics: Diagnostic[], }; +/** IDEA's highlighting levels. */ +export type InspectionLevel = "error" | "warning" | "weakWarning" | "serverProblem" | "typo" | +/** Reported (Problems, Inspect Code) but not highlighted in the editor. */ +"none"; + +/** Changes to one inspection; absent fields keep the inspection's defaults. */ +export type InspectionOverride = { + enabled?: boolean | null, + severity?: InspectionLevel | null, +}; + +export type InspectionSettings = { + /** + * Profile in effect for the project: `default`, `project`, or a copy + * as `ide:` / `project:`. + */ + profile?: string, + /** The `Default` profile (IDE level): inspection id → override. */ + default?: { [key in string]: InspectionOverride }, + /** The `Project Default` profile (stored with the project). */ + project?: { [key in string]: InspectionOverride }, + /** Profiles copied at IDE level (global settings), by key. */ + ideProfiles?: { [key in string]: CustomInspectionProfile }, + /** Profiles copied at project level (project settings), by key. */ + projectProfiles?: { [key in string]: CustomInspectionProfile }, +}; + /** A command line that installs a missing tool (run in the Run tool window). */ export type InstallCommand = { command: string, @@ -2578,6 +2883,12 @@ export type LanguageInfo_Deserialize = { indent: IndentConfig_Deserialize | null, /** A debug adapter is configured (breakpoints make sense in these files). */ debuggable?: boolean, + /** Names of the configured language servers (Settings › Inspections). */ + languageServers?: string[], + /** Exact file names and file-name patterns of the definition (Settings › File Types). */ + filenames?: string[], + /** First-line regex of the definition (shebangs, ``). */ +export type LibraryGroup = { + /** Display name, e.g. `Python 3.12 (.venv)`. */ + name: string, + /** `python` | `jdk` | `nodeModules` | `nodeCore` | `rust` | `cargo` | `go` | `goModules` | `maven` | `gradle`. */ + kind: string, + roots: LibraryRoot[], +}; + +export type LibraryRoot = { + /** + * Folder name, e.g. `python3.12` or `site-packages`, the package (`serde 1.0.200`) + * or the archive (`src.zip`, `guava-33.0.0-jre-sources.jar`). + */ + name: string, + /** A folder, or an archive's root (`!`). */ + path: IpcPath, +}; + /** Coverage of one source line. */ export type LineCoverage = { /** 0-based. */ @@ -2740,6 +3076,13 @@ export type LogLocation = { file: IpcPath | null, }; +/** Order of the Git Log (Graph Options › Sort). */ +export type LogOrder = +/** `--topo-order`: lines of history are not intermixed. */ +"topo" | +/** `--date-order`: commit time, but never a parent before its children. */ +"date"; + export type LogQuery = { branch: string | null, path: IpcPath | null, @@ -2760,10 +3103,34 @@ export type LogQuery = { branches?: string[], /** More author needles next to `author`; a commit matches any of them (Log User filter). */ authors?: string[], + order?: LogOrder, + /** Follow only the first parent of merges (`--first-parent`). */ + firstParent?: boolean, + /** Leave out merge commits (`--no-merges`); their children link to their parents. */ + noMerges?: boolean, + /** + * Committer time bounds (epoch ms; `since` inclusive, `until` exclusive). Parents + * outside the range are rewritten to their nearest shown ancestors (or dropped when + * older), so the graph stays connected. + */ + sinceMs?: number | null, + untilMs?: number | null, }; export type LspRefreshKind = "inlayHints" | "semanticTokens" | "codeLens"; +/** + * Main toolbar entries per side, in order: title widget ids (`project`, + * `vcs.branch`…) or `action:`. Widgets in neither list nor `hidden` + * (registered later) keep their default place. + */ +export type MainToolbarSettings = { + left?: string[], + right?: string[], + /** Widgets the user removed. */ + hidden?: string[], +}; + export type Markup = { /** `markdown` or `plaintext`. */ kind: string, @@ -2864,6 +3231,8 @@ export type MenuConfig = { labels: { [key in string]: string }, /** Action id → display shortcut from the JS keymap, e.g. `⇧⌘O`. */ shortcuts: { [key in string]: string }, + /** [`Entry::Dynamic`] slot → its items, already localised. */ + dynamic?: { [key in string]: DynamicItem[] }, }; /** @@ -2876,6 +3245,16 @@ export type MenuNode = { kind: "submenu"; id: string; label: string; children: M /** A disabled placeholder such as "No Recent Projects". */ { kind: "placeholder"; id: string; label: string } | { kind: "separator" }; +/** Merge dialog › Modify options (shared by the Pull dialog). */ +export type MergeOptions = { + noFf?: boolean, + ffOnly?: boolean, + squash?: boolean, + noCommit?: boolean, + /** `-m`: message of the merge commit (Merge dialog only). */ + message?: string | null, +}; + /** * Who stands on each side of the conflicts of the operation in progress (merge editor * titles, as IDEA labels them). @@ -2996,6 +3375,8 @@ export type OutlineItem = { /** The identifier itself; where navigation lands. */ selection_range: Range, children: OutlineItem[], + /** Unknown (`None`) for language-server symbols and languages without the notion. */ + visibility?: Visibility | null, }; export type OutputChunk = { kind: "stdout"; text: string } | { kind: "stderr"; text: string } | @@ -3317,6 +3698,10 @@ export type ProjectStructureSettings = { excluded?: string[], /** Auto-detected roots (from `pom.xml`, `build.gradle`, `Cargo.toml`, …) the user unmarked. */ unmarked?: string[], + /** Content roots besides the project folder (absolute paths), shown and searched with it. */ + contentRoots?: string[], + /** "Exclude files": `;`-separated name patterns (`*.log;tmp*`) excluded at any depth. */ + excludeFiles?: string, }; export type PruneTarget = "containers" | @@ -3410,6 +3795,14 @@ export type RebaseEntry = { message?: string | null, }; +/** Rebase dialog options. */ +export type RebaseOptions = { + /** `--onto `: replay `upstream..HEAD` onto this revision instead. */ + onto?: string | null, + rebaseMerges?: boolean, + keepEmpty?: boolean, +}; + export type RecentProject = { path: IpcPath, name: string, @@ -3418,6 +3811,17 @@ export type RecentProject = { branch: string | null, }; +export type Remote = { + name: string, + /** Fetch URL. */ + url: string, +}; + +/** Manage Remotes edits. */ +export type RemoteEdit = { op: "add"; name: string; url: string } | { op: "remove"; name: string } | +/** Renames `name` to `new_name` (when different) and sets its URL. */ +{ op: "edit"; name: string; new_name: string; url: string }; + export type RemoteOp = { op: "fetch" } | { op: "pull"; rebase: boolean } | { op: "push"; force_with_lease: boolean; set_upstream: boolean; /** Push dialog › Push tags. */ tags?: PushTags | null } | @@ -3429,7 +3833,9 @@ tags?: PushTags | null } | */ { op: "pushBranch"; branch: string; revision: string | null; force_with_lease: boolean; target?: string | null } | /** Fast-forwards local `branch` (not checked out) to its upstream. */ -{ op: "updateBranch"; branch: string }; +{ op: "updateBranch"; branch: string } | +/** Pull dialog: `branches` of `remote` (none = the configured merge branch). */ +{ op: "pullFrom"; remote: string; branches: string[]; rebase: boolean; options: MergeOptions }; export type ReplaceEvent = /** A file was changed (`replacements` > 0) or had nothing left to replace. */ @@ -3492,7 +3898,7 @@ export type RunConfig_Deserialize = { debugAdapter: string | null, /** "Allow multiple instances": Run on a running config starts another one instead of restarting it. */ allowParallel?: boolean, -}; +} & RunExtras_Deserialize; export type RunConfig_Serialize = { /** Stable id, e.g. `npm:dev`, `cargo:run:app`, or a uuid for user-defined. */ @@ -3512,6 +3918,78 @@ export type RunConfig_Serialize = { debugAdapter: string | null, /** "Allow multiple instances": Run on a running config starts another one instead of restarting it. */ allowParallel?: boolean, +} & RunExtras_Serialize; + +export type RunExtras = RunExtras_Serialize | RunExtras_Deserialize; + +export type RunExtras_Deserialize = { + /** Values of the configuration type's own form fields (the command line derives from them). */ + params?: { [key in string]: string }, + /** Folder in the Run/Debug Configurations tree. */ + folder?: string | null, + /** Stored in the workspace (`.openidea/run.local.toml`, not shared) instead of `run.toml`. */ + local?: boolean, + /** Off: the process gets only the configured variables. */ + includeSystemEnv?: boolean, + /** `.env` files (relative to the project root) loaded before `env`, in order. */ + envFiles?: string[], + /** "Redirect input from": the process reads this file as stdin. */ + stdinFile?: string | null, + /** "Save console output to file" (overwritten on each run). */ + outputFile?: string | null, + beforeLaunch?: BeforeLaunchTask[], + /** Compound configurations: ids of the configurations started together. */ + compound?: string[], + showOnStdout?: boolean, + showOnStderr?: boolean, + /** Overrides Settings › Run › "Show the Run tool window when a process starts". */ + openToolWindow?: boolean | null, + focusToolWindow?: boolean, + /** + * "Emulate terminal in output console": the process runs on a pseudo-terminal + * (colours, line editing, full-screen programs); the console renders it as a terminal. + */ + emulateTerminal?: boolean, + /** + * Shell command line run on Stop before the process is signalled (e.g. `docker stop `), + * with the run's working directory and environment. + */ + stopCommand?: string | null, +}; + +export type RunExtras_Serialize = { + /** Values of the configuration type's own form fields (the command line derives from them). */ + params?: { [key in string]: string }, + /** Folder in the Run/Debug Configurations tree. */ + folder?: string | null, + /** Stored in the workspace (`.openidea/run.local.toml`, not shared) instead of `run.toml`. */ + local?: boolean, + /** Off: the process gets only the configured variables. */ + includeSystemEnv?: boolean, + /** `.env` files (relative to the project root) loaded before `env`, in order. */ + envFiles?: string[], + /** "Redirect input from": the process reads this file as stdin. */ + stdinFile?: string | null, + /** "Save console output to file" (overwritten on each run). */ + outputFile?: string | null, + beforeLaunch?: BeforeLaunchTask[], + /** Compound configurations: ids of the configurations started together. */ + compound?: string[], + showOnStdout?: boolean, + showOnStderr?: boolean, + /** Overrides Settings › Run › "Show the Run tool window when a process starts". */ + openToolWindow?: boolean | null, + focusToolWindow?: boolean, + /** + * "Emulate terminal in output console": the process runs on a pseudo-terminal + * (colours, line editing, full-screen programs); the console renders it as a terminal. + */ + emulateTerminal?: boolean, + /** + * Shell command line run on Stop before the process is signalled (e.g. `docker stop `), + * with the run's working directory and environment. + */ + stopCommand?: string | null, }; /** A process started from a run configuration. */ @@ -3523,6 +4001,8 @@ export type RunInfo = { name: string, pid: number | null, running: boolean, + /** Runs on a pseudo-terminal: output is raw terminal data, input goes in keystroke by keystroke. */ + pty: boolean, }; /** Run/Debug configuration behaviour. */ @@ -3615,7 +4095,12 @@ export type SearchScope = { kind: "project" } | { kind: "directory"; path: IpcPa * IDEA's named scopes over the project's roots ("Project Production Files" …). * Walks the project like `Project`; the caller passes the matching [`PathFilter`]. */ -{ kind: "fileSet"; set: ProjectFileSet }; +{ kind: "fileSet"; set: ProjectFileSet } | +/** + * A custom scope: the project's files matching a [`ScopePattern`] + * (`file:src//*.ts&&!file:*test*`). + */ +{ kind: "pattern"; pattern: string }; export type SearchSummary = { files: number, @@ -3671,6 +4156,22 @@ export type SessionInfo = { export type Settings = Settings_Serialize | Settings_Deserialize; +export type SettingsImport = SettingsImport_Serialize | SettingsImport_Deserialize; + +export type SettingsImport_Deserialize = { + /** Effective settings of the calling window after the import. */ + settings: Settings_Deserialize, + /** IDE state the frontend keeps (written back to its storage). */ + storage: { [key in string]: string }, +}; + +export type SettingsImport_Serialize = { + /** Effective settings of the calling window after the import. */ + settings: Settings_Serialize, + /** IDE state the frontend keeps (written back to its storage). */ + storage: { [key in string]: string }, +}; + export type SettingsScope = "global" | "project"; export type Settings_Deserialize = { @@ -3709,6 +4210,10 @@ export type Settings_Deserialize = { * command the language definition launches (`elixir-ls`, `codelldb`). */ tools?: { [key in string]: ToolSettings }, + /** Editor › Inspections profiles. */ + inspections?: InspectionSettings, + /** Editor › Natural Languages › Spelling. */ + spelling?: SpellingSettings, }; export type Settings_Serialize = { @@ -3747,6 +4252,10 @@ export type Settings_Serialize = { * command the language definition launches (`elixir-ls`, `codelldb`). */ tools: { [key in string]: ToolSettings }, + /** Editor › Inspections profiles. */ + inspections: InspectionSettings, + /** Editor › Natural Languages › Spelling. */ + spelling: SpellingSettings, }; export type Severity = "error" | "warning" | "info" | "hint"; @@ -3804,6 +4313,16 @@ export type SourceFolder = { detectedFrom: string | null, }; +/** Editor › Natural Languages › Spelling. */ +export type SpellingSettings = { + /** Application-level dictionary: accepted words (IDE settings). */ + ideWords?: string[], + /** Project-level dictionary: accepted words (project settings). */ + projectWords?: string[], + /** Custom dictionaries: plain word lists or Hunspell `.dic` files. */ + customDictionaries?: string[], +}; + export type SqlStatement = { /** UTF-16 offsets of the statement text (whitespace trimmed, `;` excluded). */ from: number, @@ -4188,6 +4707,13 @@ export type TypeScriptSettings = { version?: string, }; +/** A misspelt word. */ +export type Typo = { + /** UTF-16 range of the word. */ + range: Range, + word: string, +}; + /** What [`FileMoves::undo`] did. */ export type UndoOutcome = { /** Moves reverted (each `from` is the original location again). */ @@ -4262,6 +4788,11 @@ export type VirtualDocument = { decompiled: boolean, }; +/** Declared (or conventional) visibility of an outline symbol. */ +export type Visibility = "public" | "protected" | +/** Package / module / assembly scope (Java default, `internal`, `pub(crate)`, Go unexported). */ +"package" | "private"; + /** * A web reference no language server resolves and where it points: `class="card"` → * `.card` selectors, `id` → `#id` selectors, `href="#top"` → the element, `var(--x)` → @@ -4301,6 +4832,18 @@ export type WorkspaceSymbol = { location: Location, }; +export type Worktree = { + path: IpcPath, + /** Checked-out commit; `None` for a bare main repository. */ + head: string | null, + /** Short branch name; `None` when HEAD is detached. */ + branch: string | null, + /** The main working tree (cannot be removed). */ + main: boolean, + locked: boolean, + prunable: boolean, +}; + /* Tauri Specta runtime */ async function typedError(result: Promise): Promise<{ status: "ok"; data: T } | { status: "error"; error: E }> { try { diff --git a/src/editor/api.ts b/src/editor/api.ts index 67bdbb2e..477396d7 100644 --- a/src/editor/api.ts +++ b/src/editor/api.ts @@ -391,6 +391,28 @@ export function _provideOpenEditorPaths(provider: () => string[]) { openPathsProvider = provider; } +// --- library sources ----------------------------------------------------------------- + +const libraryRoots = new Map(); + +/** + * Folders of library / SDK sources ("External Libraries") contributed by `owner`; + * files below them open read-only, like IDEA's library sources. Replaces the + * owner's previous roots; returns a disposer. + */ +export function setLibraryRoots(owner: string, roots: readonly string[]): () => void { + libraryRoots.set(owner, roots); + return () => { + if (libraryRoots.get(owner) === roots) libraryRoots.delete(owner); + }; +} + +export function isLibraryPath(path: string): boolean { + for (const roots of libraryRoots.values()) + for (const r of roots) if (path === r || path.startsWith(r.endsWith("/") ? r : `${r}/`)) return true; + return false; +} + // --- documents shown outside editor tabs --------------------------------------- /** diff --git a/src/editor/core/actions.ts b/src/editor/core/actions.ts index 29d11845..f03ac4f8 100644 --- a/src/editor/core/actions.ts +++ b/src/editor/core/actions.ts @@ -36,7 +36,9 @@ import { cyclicExpandWord } from "./wordCompletion"; import { qualifiedReference } from "./copyReference"; import { symbolChain } from "./view/outline"; import { recordClipboard } from "./clipboardHistory"; -import { useOutline } from "./model/document"; +import { useDocMeta, useOutline } from "./model/document"; +import { fontZoom, setFontZoom } from "./cm/prefs"; +import { useEditorRegistry } from "@/editor/api"; import { useProject } from "@/platform/project"; import { toggleReadOnly } from "./fileAttributes"; import { openNavBarPopup } from "./view/NavBarPopup"; @@ -410,6 +412,32 @@ export function registerEditorActions(): () => void { return ctrl.reloadFromDisk(paths.length ? paths : active ? [active] : []); }, }, + { + // IDEA's Synchronize: open documents without unsaved changes, the project tree and VCS status. + id: "file.reloadAllFromDisk", + title: "Reload All from Disk", + category: "File", + icon: "refresh", + when: "projectOpen", + keywords: ["synchronize", "refresh"], + run: async () => { + const meta = useDocMeta.getState(); + await ctrl.reloadFromDisk(ctrl.openPaths().filter((p) => !meta[p]?.dirty)); + void executeAction("project.refresh"); + void executeAction("vcs.refresh"); + }, + }, + { + id: "editor.resetFontSize", + title: "Reset Font Size in All Editors", + category: "View", + keywords: ["zoom", "font size", "mouse wheel"], + when: "projectOpen", + run: () => { + for (const ed of useEditorRegistry.getState().editors) + if (ed.view.state.field(fontZoom, false)) ed.view.dispatch({ effects: setFontZoom.of(0) }); + }, + }, { id: "file.toggleReadOnly", title: "Toggle Read-only Attribute", diff --git a/src/editor/core/backend.ts b/src/editor/core/backend.ts index e075ec6e..6ac951d5 100644 --- a/src/editor/core/backend.ts +++ b/src/editor/core/backend.ts @@ -52,6 +52,8 @@ export interface DocBackend { onDirty(handler: (e: { doc: DocId; dirty: boolean }) => void): () => void; /** Files changed on disk (used to refresh nothing but closed-file state). */ onFsChanged?(handler: (e: { removed: string[] }) => void): () => void; + /** An open document's language changed (Settings › File Types, Override File Type). */ + onLanguage?(handler: (e: { doc: DocId; language: string }) => void): () => void; } export const ipcBackend: DocBackend = { @@ -71,6 +73,7 @@ export const ipcBackend: DocBackend = { onPatched: (h) => onIdeEvent("docPatched", (e) => h(e)), onDirty: (h) => onIdeEvent("docDirty", (e) => h(e)), onFsChanged: (h) => onIdeEvent("fsChanged", (e) => h(e)), + onLanguage: (h) => onIdeEvent("docLanguage", (e) => h(e)), }; // --------------------------------------------------------------------------- diff --git a/src/editor/core/cm/codeLens.ts b/src/editor/core/cm/codeLens.ts index b5dfe064..d417c5ec 100644 --- a/src/editor/core/cm/codeLens.ts +++ b/src/editor/core/cm/codeLens.ts @@ -55,6 +55,7 @@ export function indentColumns(text: string, tabSize: number): number { const ICONS: Record = { run: "M4 3l9 5-9 5z", + user: "M8 2a3 3 0 110 6 3 3 0 010-6zM2.5 14c0-3 2.5-5 5.5-5s5.5 2 5.5 5z", debug: "M8 2a3 3 0 013 3v1h1v2h-1v1h1v2h-1a3 3 0 01-6 0H4V9h1V8H4V6h1V5a3 3 0 013-3z", }; diff --git a/src/editor/core/cm/find.test.ts b/src/editor/core/cm/find.test.ts index 94cd53d5..6fa3c52f 100644 --- a/src/editor/core/cm/find.test.ts +++ b/src/editor/core/cm/find.test.ts @@ -2,8 +2,14 @@ import { afterEach, describe, expect, it } from "vitest"; import { EditorState } from "@codemirror/state"; import { EditorView } from "@codemirror/view"; import { setSearchQuery } from "@codemirror/search"; +import { StreamLanguage } from "@codemirror/language"; +import { literalKindAt } from "./literals"; import { countMatches, + excludeCurrent, + findExcluded, + isExcluded, + toggleFindExcluded, findExtension, findNext, findPrevious, @@ -84,3 +90,70 @@ describe("find in selection", () => { expect(mapScope({ from: 2, to: 5 }, s.changes({ from: 2, to: 5 }))).toBeNull(); }); }); + +describe("search context, exclude and preserve case", () => { + const lang = StreamLanguage.define({ + token(stream) { + if (stream.match("//")) { + stream.skipToEnd(); + return "comment"; + } + if (stream.match(/^"[^"]*"/)) return "string"; + stream.next(); + return null; + }, + }); + const code = 'foo("foo") // foo\nfoo'; + const base = { search: "", caseSensitive: false, wholeWord: false, regexp: false, replace: "" }; + function makeCode(o: Partial) { + const view = new EditorView({ + state: EditorState.create({ doc: code, extensions: [lang, findExtension()] }), + parent: document.body, + }); + views.push(view); + view.dispatch({ effects: setSearchQuery.of(makeQuery({ ...base, search: "foo", ...o })) }); + return view; + } + const starts = (v: EditorView) => countMatches(v.state).starts; + + it("filters matches by comment / string literal", () => { + expect(starts(makeCode({}))).toEqual([0, 5, 14, 18]); + expect(starts(makeCode({ context: "inComments" }))).toEqual([14]); + expect(starts(makeCode({ context: "inStringLiterals" }))).toEqual([5]); + expect(starts(makeCode({ context: "exceptComments" }))).toEqual([0, 5, 18]); + expect(starts(makeCode({ context: "exceptCommentsAndStringLiterals" }))).toEqual([0, 18]); + expect(literalKindAt(makeCode({}).state, 14)).toBe("comment"); + }); + + it("excluded matches are skipped by next and replace all, until the query changes", () => { + const v = makeCode({ replace: "bar" }); + findNext(v); + expect(sel(v)).toEqual([0, 3]); + excludeCurrent(v); + expect(sel(v)).toEqual([5, 8]); + expect(isExcluded(v.state, 0, 3)).toBe(true); + expect(countMatches(v.state).total).toBe(3); + replaceAll(v, false); + expect(v.state.doc.toString()).toBe('foo("bar") // bar\nbar'); + v.dispatch({ effects: setSearchQuery.of(makeQuery({ ...base, search: "o" })) }); + expect(v.state.field(findExcluded)).toEqual([]); + }); + + it("excluding an excluded match includes it again", () => { + const v = makeCode({}); + v.dispatch({ effects: toggleFindExcluded.of({ from: 0, to: 3 }), selection: { anchor: 0, head: 3 } }); + excludeCurrent(v); + expect(isExcluded(v.state, 0, 3)).toBe(false); + }); + + it("preserve case keeps each occurrence's case", () => { + const v = make("foo Foo FOO", { search: "foo", replace: "bar" }); + replaceAll(v, true); + expect(v.state.doc.toString()).toBe("bar Bar BAR"); + const w = make("Foo foo", { search: "foo", replace: "bar" }); + findNext(w); + replaceNext(w, true); + expect(w.state.doc.toString()).toBe("Bar foo"); + expect(sel(w)).toEqual([4, 7]); + }); +}); diff --git a/src/editor/core/cm/find.ts b/src/editor/core/cm/find.ts index 0d28b3b0..6c8aedde 100644 --- a/src/editor/core/cm/find.ts +++ b/src/editor/core/cm/find.ts @@ -3,7 +3,9 @@ * @codemirror/search's query + commands; the bar itself is a React component * (view/FindBar.tsx). Highlighting is ours because CodeMirror only highlights * while its own panel is open. Match positions found by the bar's counter are - * kept in `findMarks` for the overview lane. + * kept in `findMarks` for the overview lane. The search context (comments / + * string literals) and excluded matches filter matches through the query's + * `test`, so CodeMirror's own commands honour them too. */ import { EditorSelection, Prec, StateEffect, StateField, type ChangeDesc, type EditorState, type Extension } from "@codemirror/state"; import { Decoration, EditorView, ViewPlugin, type DecorationSet, type ViewUpdate } from "@codemirror/view"; @@ -18,6 +20,8 @@ import { setSearchQuery, } from "@codemirror/search"; import { scrollbarMarks, type ScrollbarMark } from "@/editor/api"; +import { contextAccepts, preserveCase, type SearchContext } from "@/platform/search"; +import { literalKindAt, literalsExtension } from "./literals"; export const setFindOpen = StateEffect.define(); export const setFindMarks = StateEffect.define(); @@ -51,6 +55,46 @@ export const findScope = StateField.define({ provide: (f) => Prec.lowest(EditorView.decorations.from(f, (s) => (s ? Decoration.set([scopeDeco.range(s.from, s.to)]) : Decoration.none))), }); +/** IDEA's Exclude: matches skipped by Next / Previous and Replace All until the query changes. */ +export const toggleFindExcluded = StateEffect.define(); + +export const findExcluded = StateField.define({ + create: () => [], + update(v, tr) { + for (const e of tr.effects) { + if (e.is(setSearchQuery) || (e.is(setFindOpen) && !e.value)) v = []; + else if (e.is(toggleFindExcluded)) { + const { from, to } = e.value; + v = v.some((x) => x.from === from && x.to === to) + ? v.filter((x) => x.from !== from || x.to !== to) + : [...v, { from, to }]; + } + } + if (!tr.docChanged || !v.length) return v; + return v + .map((x) => ({ from: tr.changes.mapPos(x.from, 1), to: tr.changes.mapPos(x.to, -1) })) + .filter((x) => x.to > x.from); + }, +}); + +export function isExcluded(state: EditorState, from: number, to: number): boolean { + return (state.field(findExcluded, false) ?? []).some((x) => x.from === from && x.to === to); +} + +type MatchTest = (match: string, state: EditorState, from: number, to: number) => boolean; +const tests = new Map(); + +/** One stable test per context, so equal options give equal queries (`SearchQuery.eq`). */ +function testFor(context: SearchContext): MatchTest { + let test = tests.get(context); + if (!test) { + test = (_match, state, from, to) => + !isExcluded(state, from, to) && (context === "any" || contextAccepts(context, literalKindAt(state, from))); + tests.set(context, test); + } + return test; +} + function scopeOf(state: EditorState): FindScope { return state.field(findScope, false) ?? { from: 0, to: state.doc.length }; } @@ -82,6 +126,7 @@ export const findMarks = StateField.define({ const matchDeco = Decoration.mark({ class: "cm-openidea-findMatch" }); const currentDeco = Decoration.mark({ class: "cm-openidea-findMatch cm-openidea-findMatch-current" }); +const excludedDeco = Decoration.mark({ class: "cm-openidea-findExcluded" }); const MAX_VISIBLE = 2000; function buildHighlights(view: EditorView): DecorationSet { @@ -104,6 +149,8 @@ function buildHighlights(view: EditorView): DecorationSet { ranges.push((m.from === main.from && m.to === main.to ? currentDeco : matchDeco).range(m.from, m.to)); } } + for (const x of state.field(findExcluded, false) ?? []) + if (x.from >= scope.from && x.to <= scope.to) ranges.push(excludedDeco.range(x.from, x.to)); return Decoration.set(ranges, true); } @@ -120,7 +167,8 @@ const findHighlighter = ViewPlugin.fromClass( u.selectionSet || u.startState.field(findOpen, false) !== u.state.field(findOpen, false) || getSearchQuery(u.startState) !== getSearchQuery(u.state) || - u.startState.field(findScope, false) !== u.state.field(findScope, false) + u.startState.field(findScope, false) !== u.state.field(findScope, false) || + u.startState.field(findExcluded, false) !== u.state.field(findExcluded, false) ) this.decorations = buildHighlights(u.view); } @@ -136,7 +184,9 @@ export function findExtension(): Extension { }), findOpen, findScope, + findExcluded, findMarks, + literalsExtension, findHighlighter, ]; } @@ -147,6 +197,10 @@ export interface FindOptions { wholeWord: boolean; regexp: boolean; replace: string; + /** IDEA's search scope filter: anywhere, only / except comments and string literals. */ + context?: SearchContext; + /** Replace keeps the case of each occurrence. */ + preserveCase?: boolean; } export function makeQuery(o: FindOptions) { @@ -158,6 +212,7 @@ export function makeQuery(o: FindOptions) { wholeWord: o.wholeWord, regexp: o.regexp, replace: o.replace, + test: testFor(o.context ?? "any"), }); } @@ -251,30 +306,56 @@ export function findPrevious(view: EditorView): boolean { return view.state.field(findScope, false) ? step(view, false) : cmFindPrevious(view); } +/** The text replacing `m`, keeping its case when `preserve` is set. */ +function insertFor(state: EditorState, query: SearchQuery, m: Match, preserve: boolean): string { + const insert = replacementFor(query, m); + return preserve ? preserveCase(state.sliceDoc(m.from, m.to), insert) : insert; +} + /** Replaces the selected match (if it is one) and selects the next match. */ -export function replaceNext(view: EditorView): boolean { +export function replaceNext(view: EditorView, preserve = false): boolean { const { state } = view; - if (!state.field(findScope, false)) return cmReplaceNext(view); + if (!state.field(findScope, false) && !preserve) return cmReplaceNext(view); if (state.readOnly) return false; const query = getSearchQuery(state); const ms = matchesIn(state); const { from, to } = state.selection.main; const cur = ms.find((m) => m.from === from && m.to === to); if (!cur) return step(view, true); - const insert = replacementFor(query, cur); - view.dispatch({ changes: { from: cur.from, to: cur.to, insert }, selection: EditorSelection.cursor(cur.from + insert.length), userEvent: "input.replace" }); - const next = matchesIn(view.state).find((m) => m.from >= cur.from + insert.length); + const insert = insertFor(state, query, cur, preserve); + view.dispatch({ + changes: { from: cur.from, to: cur.to, insert }, + selection: EditorSelection.cursor(cur.from + insert.length), + userEvent: "input.replace", + }); + const after = matchesIn(view.state); + const next = after.find((m) => m.from >= cur.from + insert.length) ?? after[0]; if (next) selectMatch(view, next); return true; } -export function replaceAll(view: EditorView): boolean { +export function replaceAll(view: EditorView, preserve = false): boolean { const { state } = view; - if (!state.field(findScope, false)) return cmReplaceAll(view); + if (!state.field(findScope, false) && !preserve) return cmReplaceAll(view); if (state.readOnly) return false; const query = getSearchQuery(state); - const changes = matchesIn(state).map((m) => ({ from: m.from, to: m.to, insert: replacementFor(query, m) })); + const changes = matchesIn(state).map((m) => ({ + from: m.from, + to: m.to, + insert: insertFor(state, query, m, preserve), + })); if (!changes.length) return false; view.dispatch({ changes, userEvent: "input.replace.all" }); return true; } + +/** IDEA's Exclude: skips the selected match (or includes it again) and selects the next one. */ +export function excludeCurrent(view: EditorView): boolean { + const { from, to } = view.state.selection.main; + const excluded = isExcluded(view.state, from, to); + if (!excluded && (from === to || !matchesIn(view.state).some((m) => m.from === from && m.to === to))) + return step(view, true); + view.dispatch({ effects: toggleFindExcluded.of({ from, to }) }); + if (!excluded) step(view, true); + return true; +} diff --git a/src/editor/core/cm/literals.ts b/src/editor/core/cm/literals.ts new file mode 100644 index 00000000..303b74bc --- /dev/null +++ b/src/editor/core/cm/literals.ts @@ -0,0 +1,95 @@ +/** + * Comments and string literals of a document, for the find bar's search + * context ("In Comments", "Except String Literals" …): from the Lezer syntax + * tree, or from the backend's tree-sitter highlights for languages without a + * CodeMirror mode. + */ +import { StateField, type EditorState } from "@codemirror/state"; +import { ensureSyntaxTree, syntaxTree } from "@codemirror/language"; +import { highlightTree, tagHighlighter, tags as t } from "@lezer/highlight"; +import type { SyntaxHighlightKind } from "@/platform/ipc"; +import type { LiteralKind } from "@/platform/search"; +import { setSyntaxHighlights } from "./syntaxHighlight"; + +/** Sorted, non-overlapping `[from, to)` ranges. */ +export interface LiteralRange { + from: number; + to: number; + kind: LiteralKind; +} + +const KINDS: Partial> = { + comment: "comment", + docComment: "comment", + string: "string", + stringSpecial: "string", + escape: "string", +}; + +/** Literal ranges of the tree-sitter highlights, mapped through edits until the next refresh. */ +const treeSitterLiterals = StateField.define({ + create: () => [], + update(v, tr) { + for (const e of tr.effects) { + if (!e.is(setSyntaxHighlights)) continue; + const { kinds, data } = e.value; + const out: LiteralRange[] = []; + for (let i = 0; i + 2 < data.length; i += 3) { + const kind = KINDS[kinds[data[i + 2]!]!]; + if (kind) push(out, data[i]!, data[i + 1]!, kind); + } + return out; + } + if (!tr.docChanged || !v.length) return v; + return v + .map((r) => ({ from: tr.changes.mapPos(r.from, 1), to: tr.changes.mapPos(r.to, -1), kind: r.kind })) + .filter((r) => r.to > r.from); + }, +}); + +export const literalsExtension = treeSitterLiterals; + +const highlighter = tagHighlighter([ + { tag: t.comment, class: "comment" }, + { tag: t.string, class: "string" }, +]); + +function push(out: LiteralRange[], from: number, to: number, kind: LiteralKind) { + const last = out[out.length - 1]; + if (last && last.kind === kind && last.to >= from) last.to = Math.max(last.to, to); + else if (to > from) out.push({ from, to, kind }); +} + +const cache = new WeakMap(); +/** How long counting may wait for the parser to reach the end of the document. */ +const PARSE_BUDGET_MS = 50; + +/** Comments and string literals of `state` (cached per syntax tree). */ +export function literalRanges(state: EditorState): readonly LiteralRange[] { + const tree = ensureSyntaxTree(state, state.doc.length, PARSE_BUDGET_MS) ?? syntaxTree(state); + if (tree.length === 0) return state.field(treeSitterLiterals, false) ?? []; + const hit = cache.get(tree); + if (hit) return hit; + const out: LiteralRange[] = []; + highlightTree(tree, highlighter, (from, to, cls) => { + const kind: LiteralKind | null = cls.includes("comment") ? "comment" : cls.includes("string") ? "string" : null; + if (kind) push(out, from, to, kind); + }); + cache.set(tree, out); + return out; +} + +/** What `pos` lies in: a comment, a string literal or plain code (`null`). */ +export function literalKindAt(state: EditorState, pos: number): LiteralKind | null { + const ranges = literalRanges(state); + let lo = 0; + let hi = ranges.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >> 1; + const r = ranges[mid]!; + if (pos < r.from) hi = mid - 1; + else if (pos >= r.to) lo = mid + 1; + else return r.kind; + } + return null; +} diff --git a/src/editor/core/cm/theme.ts b/src/editor/core/cm/theme.ts index 8e75a9d9..57cfb92e 100644 --- a/src/editor/core/cm/theme.ts +++ b/src/editor/core/cm/theme.ts @@ -176,6 +176,10 @@ export const openideaTheme = EditorView.theme({ backgroundColor: "var(--editor-search)", borderRadius: "2px", }, + ".cm-openidea-findExcluded": { + textDecoration: "line-through", + textDecorationColor: "var(--fg-muted)", + }, ".cm-openidea-findScope": { backgroundColor: "var(--editor-selection-inactive)", }, diff --git a/src/editor/core/controller.ts b/src/editor/core/controller.ts index ea49d27b..de03f1b5 100644 --- a/src/editor/core/controller.ts +++ b/src/editor/core/controller.ts @@ -7,7 +7,7 @@ import { EditorSelection } from "@codemirror/state"; import { create } from "zustand"; import { setContext } from "@/platform/context"; import { shortcutLabel } from "@/platform/keymap"; -import { useProject } from "@/platform/project"; +import { basename, useProject } from "@/platform/project"; import { editorPrefs } from "@/platform/editorSettings"; import { _emitEditorEvent, @@ -50,6 +50,7 @@ import { removePane, reopenClosed, setActivePane, + sortTabs, splitPane, tabOptions, togglePin, @@ -130,6 +131,11 @@ export function openFilePaths(s: TabsState): string[] { return out; } +/** Editor Tabs › Sort tabs alphabetically: by file name. */ +function tabSortKey(input: EditorInput): string { + return basename(input.path ?? input.key); +} + function isTextTab(tab: Tab | null | undefined): tab is Tab & { input: { path: string } } { return !!tab && isTextInput(tab.input); } @@ -776,6 +782,7 @@ export class EditorController implements SlotHost { }); s = r.state; tabId = r.tabId; + if (tabs.sortAlphabetically) s = sortTabs(s, paneId, (t) => tabSortKey(t.input)); s = enforceTabLimit( s, paneId, diff --git a/src/editor/core/index.ts b/src/editor/core/index.ts index 85dc3479..ac7433e2 100644 --- a/src/editor/core/index.ts +++ b/src/editor/core/index.ts @@ -6,15 +6,18 @@ import { provideEditorArea, provideEditorService, + registerEditorBanner, registerOverlay, registerStatusItem, } from "@/workbench/contributions"; -import { _provideCodePreview, _provideDocumentAttacher } from "@/editor/api"; +import { _provideCodePreview, _provideDocumentAttacher, editorsFor } from "@/editor/api"; +import { formatDocument } from "@/editor/lsp/format"; import { editorController } from "./controller"; import { attachDocument } from "./model/attachment"; import { previewExtensions } from "./cm/preview"; import { registerEditorActions } from "./actions"; import { installColorScheme } from "./colorSchemeRuntime"; +import { documentBanner } from "./view/Banner"; import { EditorAreaView } from "./view/EditorArea"; import { GotoLineDialog } from "./view/GotoLineDialog"; import { NavBarPopup } from "./view/NavBarPopup"; @@ -41,6 +44,10 @@ export function activate(): void | (() => void) { openPaths: () => ctrl.openPaths(), closePath: (path) => ctrl.closePath(path), closeInput: (key) => ctrl.closeInput(key), + reformat: async (path) => { + const handle = editorsFor(path)[0]; + return !!handle && (await formatDocument(handle.view, { explicit: false, selectionOnly: false })).applied; + }, }); _provideDocumentAttacher((path) => attachDocument(ctrl.manager, path)); _provideCodePreview((path) => previewExtensions(path, ctrl.manager.settings()?.editor)); @@ -48,6 +55,7 @@ export function activate(): void | (() => void) { installColorScheme(), provideEditorArea(EditorAreaView), registerEditorActions(), + registerEditorBanner(documentBanner), registerOverlay({ id: "editor.gotoLine", component: GotoLineDialog }), registerOverlay({ id: "editor.navigationBar", component: NavBarPopup }), installClipboardTracking(), diff --git a/src/editor/core/model/document.ts b/src/editor/core/model/document.ts index 981289c9..af36f5e0 100644 --- a/src/editor/core/model/document.ts +++ b/src/editor/core/model/document.ts @@ -11,6 +11,7 @@ import type { DocId, DocumentInfo, EditorSettings, FileSettings, LineEnding, Out import { _emitEditorEvent, _saveParticipants, + isLibraryPath, mirroredChange, remoteChange, useEditorRegistry, @@ -44,7 +45,7 @@ import { import { setMethodLines, softWrapActive } from "../cm/prefs"; import type { EditorSlot } from "./slot"; -export type Banner = "binary" | "tooLarge" | "readOnly" | null; +export type Banner = "binary" | "tooLarge" | "readOnly" | "library" | null; /** A view attached to a document outside the tab system (see `attachment.ts`). */ export interface DocAttachmentTarget { @@ -73,7 +74,9 @@ export const useOutline = create>(() => ({})); const REFRESH_DELAY = 450; function bannerFor(info: DocumentInfo): Banner { // Virtual documents (library classes) get their own notice from the editor area. - if (!info.readOnly || info.uri) return null; + if (info.uri) return null; + if (isLibraryPath(info.path)) return "library"; + if (!info.readOnly) return null; if (!info.text.length) return "binary"; // The backend makes files above its size limit read-only and flags them `large`. if (info.large) return "tooLarge"; @@ -181,6 +184,8 @@ export class DocModel { editorconfig: EditorConfigProps | null = null; desc: LanguageDescription | null; language: LanguageSupport | null; + /** Too large for language support (no CodeMirror language, backend highlighting only). */ + private large: boolean; lastFocused: EditorSlot | null = null; /** Bumped by every change (local or remote). */ changeCounter = 0; @@ -200,6 +205,7 @@ export class DocModel { this.editorconfig = info.editorconfig ?? null; // `large`: the backend skips LSP and syntax trees for it; heavy UI features follow. const large = !!info.large || info.text.length > HEAVY_LIMIT; + this.large = large; this.desc = large ? null : findLanguageDescription(this.info?.uiMode ?? null, info.path); this.language = loadedSupport(this.desc); this.meta = { @@ -209,7 +215,8 @@ export class DocModel { uiMode: this.info?.uiMode ?? this.desc?.name ?? null, encoding: info.encoding, lineEnding: info.lineEnding, - readOnly: info.readOnly, + // Library sources (External Libraries) open read-only, like IDEA's. + readOnly: info.readOnly || (!this.virtual && isLibraryPath(info.path)), dirty: info.dirty, banner: bannerFor(info), indent: { tabSize: 4, insertSpaces: true }, @@ -322,6 +329,23 @@ export class DocModel { this.scheduleRefresh(); } + /** The backend detected another language (Settings › File Types, Override File Type). */ + setLanguage(language: string) { + if (this.closed || language === this.meta.language) return; + this.info = languageInfo(language); + this.desc = this.large ? null : findLanguageDescription(this.info?.uiMode ?? null, this.path); + this.language = loadedSupport(this.desc); + this.highlights = null; + this.setMeta({ language, uiMode: this.info?.uiMode ?? this.desc?.name ?? null }); + for (const s of this.slots) { + s.reconfigure([compartments.language.reconfigure(this.language ?? [])]); + s.syncContributions(true); + } + this.applySettings(); + if (this.desc && !this.language) void this.loadLanguage(); + this.scheduleRefresh(0); + } + async loadLanguage() { if (!this.desc) return; const support = await loadSupport(this.desc); @@ -397,7 +421,7 @@ export class DocModel { setReadOnly(readOnly: boolean) { if (readOnly === this.meta.readOnly) return; const banner = this.meta.banner; - this.setMeta({ readOnly, banner: readOnly ? (banner ?? (this.virtual ? null : "readOnly")) : banner === "readOnly" ? null : banner }); + this.setMeta({ readOnly, banner: readOnly ? (banner ?? (this.virtual ? null : "readOnly")) : banner === "readOnly" || banner === "library" ? null : banner }); for (const s of this.slots) { s.reconfigure([ compartments.readOnly.reconfigure(readOnlyExtension(readOnly)), @@ -570,6 +594,7 @@ export class DocumentManager { start() { this.disposers.push( this.backend.onPatched((e) => this.byId.get(e.doc)?.sync.remote(e)), + this.backend.onLanguage?.((e) => this.byId.get(e.doc)?.setLanguage(e.language)) ?? (() => {}), this.backend.onDirty((e) => { const m = this.byId.get(e.doc); if (m && m.getMeta().dirty !== e.dirty) { diff --git a/src/editor/core/model/model.test.ts b/src/editor/core/model/model.test.ts index 360d0dfe..b432a8eb 100644 --- a/src/editor/core/model/model.test.ts +++ b/src/editor/core/model/model.test.ts @@ -15,6 +15,7 @@ import { paneIds, promoteTab, reopenClosed, + sortTabs, splitPane, tabOptions, togglePin, @@ -181,6 +182,15 @@ describe("tab model", () => { s = cycleTab(s, -1); expect(active(s)).toBe("c"); }); + + it("sorts tabs alphabetically, pinned ones first", () => { + let s = initialTabsState(); + for (const p of ["c", "B", "a", "d"]) s = open(s, p); + s = togglePin(s, s.activePane, s.panes[s.activePane]!.tabs.find((t) => t.input.path === "d")!.id); + s = sortTabs(s, s.activePane, (t) => t.input.path!); + expect(labels(s)).toEqual(["d^", "a", "B", "c"]); + expect(sortTabs(s, s.activePane, (t) => t.input.path!)).toBe(s); + }); }); describe("navigation history", () => { diff --git a/src/editor/core/model/tabs.ts b/src/editor/core/model/tabs.ts index 7a460c20..5e6ef31f 100644 --- a/src/editor/core/model/tabs.ts +++ b/src/editor/core/model/tabs.ts @@ -320,6 +320,15 @@ function rank(pane: Pane, tab: Tab): number { return i < 0 ? Number.MAX_SAFE_INTEGER : i; } +/** Editor Tabs › Sort tabs alphabetically: orders the pinned and the other tabs of a pane by `title`. */ +export function sortTabs(s: TabsState, paneId: string, title: (t: Tab) => string): TabsState { + const pane = paneOf(s, paneId); + const byTitle = (a: Tab, b: Tab) => + title(a).localeCompare(title(b), undefined, { sensitivity: "base" }) || title(a).localeCompare(title(b)); + const tabs = [...pane.tabs.filter((t) => t.pinned).sort(byTitle), ...pane.tabs.filter((t) => !t.pinned).sort(byTitle)]; + return tabs.every((t, i) => t === pane.tabs[i]) ? s : withPane(s, { ...pane, tabs }); +} + export function cycleTab(s: TabsState, delta: number, paneId = s.activePane): TabsState { const pane = paneOf(s, paneId); if (pane.tabs.length < 2) return s; diff --git a/src/editor/core/view/Banner.tsx b/src/editor/core/view/Banner.tsx index 334fb8bb..daa147df 100644 --- a/src/editor/core/view/Banner.tsx +++ b/src/editor/core/view/Banner.tsx @@ -1,4 +1,6 @@ -import { Icon } from "@/ui"; +import { useMemo } from "react"; +import { Icon, T } from "@/ui"; +import { useEditorBanners, type EditorBannerContribution } from "@/workbench/contributions"; import { useDocMeta } from "../model/document"; import styles from "./EditorArea.module.css"; @@ -6,19 +8,18 @@ const TEXT = { binary: "This file appears to be binary and is shown read-only.", tooLarge: "The file is too large: editing and code insight are disabled.", readOnly: "The file is read-only.", + library: "Library source (read-only).", } as const; /** Notice above read-only / binary / too-large documents. */ -export function Banner({ path }: { path: string }) { - const banner = useDocMeta((s) => s[path]?.banner ?? null); - if (!banner) return null; - return ( -

- - {TEXT[banner]} -
- ); -} +export const documentBanner: EditorBannerContribution = { + id: "editor.document", + order: -10, + useBanner: (path) => { + const banner = useDocMeta((s) => s[path]?.banner ?? null); + return banner ? { text: TEXT[banner], icon: banner === "readOnly" || banner === "library" ? "lock" : "info" } : null; + }, +}; /** * Notice above a library class opened from a language server (IDEA's @@ -39,3 +40,38 @@ export function VirtualBanner({ decompiled, description }: { decompiled: boolean ); } + +function ContributedBanner({ banner, path }: { banner: EditorBannerContribution; path: string }) { + const b = banner.useBanner(path); + if (!b) return null; + return ( +
+ + + {b.text} + + + {b.actions?.map((a) => ( + + ))} +
+ ); +} + +/** Banners features contribute for `path` (`registerEditorBanner`), in order. */ +export function EditorBanners({ path }: { path: string }) { + const map = useEditorBanners((s) => s.banners); + const banners = useMemo( + () => [...map.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100) || a.id.localeCompare(b.id)), + [map], + ); + return ( + <> + {banners.map((b) => ( + + ))} + + ); +} diff --git a/src/editor/core/view/EditorArea.module.css b/src/editor/core/view/EditorArea.module.css index 6c2c1509..3455430c 100644 --- a/src/editor/core/view/EditorArea.module.css +++ b/src/editor/core/view/EditorArea.module.css @@ -217,19 +217,45 @@ .tabsHidden { display: none; } -.tabsMultiRow { - height: auto; - min-height: var(--h-header); +.tabRows { + flex: none; + display: flex; + flex-direction: column; + min-width: 0; } -.tabsMultiRow > [role="tablist"] { - flex: 1; - flex-wrap: wrap; - overflow: visible; - row-gap: 2px; - padding: 3px 0; +.tabsBottom > .tabs, +.tabsBottom > .tabRows { + order: 1; } -.tabsMultiRow > [role="tablist"] > * { - height: calc(var(--h-header) - 6px); +.tabsBottom .tabs { + border-bottom: none; + border-top: 1px solid var(--border-editor-tabs, var(--border)); +} +/* Tabs on the left / right of the editor (a column). */ +.tabsVertical { + flex-direction: row; +} +.tabsVertical > .tabs, +.tabsVertical > .tabRows { + width: max-content; + min-width: 140px; + max-width: 260px; + border-bottom: none; + border-right: 1px solid var(--border-editor-tabs, var(--border)); +} +.tabsVertical .tabRows > .tabs { + min-height: 0; + border-bottom: none; +} +.tabsVertical .tabRows > .tabs + .tabs { + flex: 1 1 auto; + border-top: 1px solid var(--border-editor-tabs, var(--border)); +} +.tabsRight > .tabs, +.tabsRight > .tabRows { + order: 1; + border-right: none; + border-left: 1px solid var(--border-editor-tabs, var(--border)); } /* Breadcrumbs inside the editor (top or bottom of the text) */ @@ -251,3 +277,18 @@ .codeCrumbsBottom { border-top: 1px solid var(--border-editor-tabs, var(--border)); } + +/* Contributed editor banners (registerEditorBanner): text, then right-aligned links. */ +.bannerLink { + flex: none; + padding: 0; + border: 0; + background: none; + color: var(--link); + font: inherit; + cursor: pointer; +} +.bannerLink:hover { + color: var(--link-hover); + text-decoration: underline; +} diff --git a/src/editor/core/view/EditorArea.tsx b/src/editor/core/view/EditorArea.tsx index a87773e5..f36f4645 100644 --- a/src/editor/core/view/EditorArea.tsx +++ b/src/editor/core/view/EditorArea.tsx @@ -42,7 +42,7 @@ import type { LayoutNode, SplitEdge, Tab } from "../model/tabs"; import { disambiguateTitles } from "../model/tabTitles"; import { TextEditorHost } from "./TextEditorHost"; import { FindBar } from "./FindBar"; -import { Banner, VirtualBanner } from "./Banner"; +import { EditorBanners, VirtualBanner } from "./Banner"; import { CodeBreadcrumbs } from "./StatusItems"; import styles from "./EditorArea.module.css"; @@ -147,12 +147,13 @@ function PaneView({ paneId }: { paneId: string }) { const paths = tabs.map((t) => t.input.path ?? ""); const dirty = useDocMeta(useShallow((s) => paths.map((p) => !!s[p]?.dirty))); const colors = useFileDecorations(useShallow(() => paths.map((p) => (p ? (getFileDecoration(p)?.color ?? "") : "")))); + const tints = useFileDecorations(useShallow(() => paths.map((p) => (p ? (getFileDecoration(p)?.tint ?? "") : "")))); const hints = useTitleHints(); const [dropZone, setDropZone] = useState(null); const tabPrefs = useEditorPrefs().tabs; const distractionFree = useDistractionFree((s) => s.active); - const { showExtension, markModified, showCloseButton } = tabPrefs; + const { showExtension, markModified, showCloseButton, showFileIcon, showDirectoryForNonUnique, showFullPathOnHover } = tabPrefs; const items: TabItem[] = useMemo(() => { const names = tabs.map((t) => tabTitle(t.input, kinds)); @@ -160,8 +161,9 @@ function PaneView({ paneId }: { paneId: string }) { const full = names[i]!; const dot = full.lastIndexOf("."); const name = t.input.kind === "file" && !showExtension && dot > 0 ? full.slice(0, dot) : full; - const dupe = - t.input.kind === "file" && t.input.path + const dupe = !showDirectoryForNonUnique + ? null + : t.input.kind === "file" && t.input.path ? (hints.get(t.input.path) ?? null) : names.filter((n) => n === name).length > 1 && t.input.path ? basename(dirname(t.input.path)) @@ -175,28 +177,47 @@ function PaneView({ paneId }: { paneId: string }) { ), text: dupe ? `${name} — ${dupe}` : name, - icon: tabIcon(t.input, kinds), + icon: showFileIcon ? tabIcon(t.input, kinds) : undefined, italic: t.preview, modified: dirty[i], closable: showCloseButton, color: colors[i] || undefined, - tooltip: virtualTooltip(t.input) ?? t.input.path ?? name, + tint: tints[i] || undefined, + tooltip: virtualTooltip(t.input) ?? (showFullPathOnHover ? (t.input.path ?? name) : undefined), trailing: t.pinned ? : undefined, }; }); - }, [tabs, kinds, dirty, colors, hints, showExtension, markModified, showCloseButton]); + }, [ + tabs, + kinds, + dirty, + colors, + tints, + hints, + showExtension, + markModified, + showCloseButton, + showFileIcon, + showDirectoryForNonUnique, + showFullPathOnHover, + ]); - const dnd: TabsDnd = useMemo( - () => ({ + const pinnedCount = tabs.filter((t) => t.pinned).length; + // Settings › Editor Tabs › Tab placement Left / Right: a vertical, scrolling column. + const vertical = tabPrefs.placement === "left" || tabPrefs.placement === "right"; + const pinnedRow = + tabPrefs.pinnedSeparateRow && (vertical || !tabPrefs.multipleRows) && pinnedCount > 0 && pinnedCount < tabs.length; + const dnd = useMemo(() => { + const strip = (offset: number): TabsDnd => ({ type: EDITOR_TAB_MIME, payload: (tabId) => JSON.stringify({ paneId, tabId }), onDrop: (raw, index) => { const p = parseTabPayload(raw); - if (p) ctrl.moveTab(p.paneId, p.tabId, paneId, index); + if (p) ctrl.moveTab(p.paneId, p.tabId, paneId, index + offset); }, - }), - [ctrl, paneId], - ); + }); + return { all: strip(0), rest: strip(pinnedCount) }; + }, [ctrl, paneId, pinnedCount]); if (!pane) return null; const active = pane.tabs.find((t) => t.id === pane.active) ?? null; @@ -236,42 +257,65 @@ function PaneView({ paneId }: { paneId: string }) { }, }; + const hidden = tabPrefs.placement === "none" || distractionFree; + const strip = (stripItems: TabItem[], stripDnd: TabsDnd, trailing: ReactNode) => ( +