From 0502a3bd4a3b70958e920e1a49511e2968c00a2c Mon Sep 17 00:00:00 2001
From: sharkdp <davidpeter@web.de>
Date: Sun, 7 Oct 2018 13:26:50 +0200
Subject: [PATCH] Add first-line detection for all input types

closes #205
---
 src/assets.rs     | 43 +++++++++++++++++++++++--------------------
 src/controller.rs | 15 +++++++++------
 src/printer.rs    | 11 ++++++++---
 3 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/src/assets.rs b/src/assets.rs
index 1ad1f876..36436f29 100644
--- a/src/assets.rs
+++ b/src/assets.rs
@@ -8,10 +8,7 @@ use syntect::dumps::{dump_to_file, from_binary, from_reader};
 use syntect::highlighting::{Theme, ThemeSet};
 use syntect::parsing::{SyntaxDefinition, SyntaxSet};
 
-#[cfg(unix)]
-use std::os::unix::fs::FileTypeExt;
-
-use inputfile::InputFile;
+use inputfile::{InputFile, InputFileReader};
 
 lazy_static! {
     static ref PROJECT_DIRS: ProjectDirs =
@@ -166,27 +163,33 @@ impl HighlightingAssets {
         }
     }
 
-    pub fn get_syntax(&self, language: Option<&str>, filename: InputFile) -> &SyntaxDefinition {
+    pub fn get_syntax(
+        &self,
+        language: Option<&str>,
+        filename: InputFile,
+        reader: &mut InputFileReader,
+    ) -> &SyntaxDefinition {
         let syntax = match (language, filename) {
             (Some(language), _) => self.syntax_set.find_syntax_by_token(language),
             (None, InputFile::Ordinary(filename)) => {
-                #[cfg(not(unix))]
-                let may_read_from_file = true;
-
-                // Do not peek at the file (to determine the syntax) if it is a FIFO because they can
-                // only be read once.
-                #[cfg(unix)]
-                let may_read_from_file = !fs::metadata(filename)
-                    .map(|m| m.file_type().is_fifo())
-                    .unwrap_or(false);
-
-                if may_read_from_file {
-                    self.syntax_set
-                        .find_syntax_for_file(filename)
-                        .unwrap_or(None)
+                let path = Path::new(filename);
+                let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
+                let extension = path.extension().and_then(|x| x.to_str()).unwrap_or("");
+                let ext_syntax = self
+                    .syntax_set
+                    .find_syntax_by_extension(file_name)
+                    .or_else(|| self.syntax_set.find_syntax_by_extension(extension));
+                let line_syntax = if ext_syntax.is_none() {
+                    reader
+                        .get_first_line()
+                        .ok()
+                        .and_then(|v| String::from_utf8(v).ok())
+                        .and_then(|l| self.syntax_set.find_syntax_by_first_line(&l))
                 } else {
                     None
-                }
+                };
+                let syntax = ext_syntax.or(line_syntax);
+                syntax
             }
             (None, InputFile::StdIn) => None,
             (_, InputFile::ThemePreviewFile) => self.syntax_set.find_syntax_by_name("Rust"),
diff --git a/src/controller.rs b/src/controller.rs
index da9b710e..ccbb6c31 100644
--- a/src/controller.rs
+++ b/src/controller.rs
@@ -23,13 +23,18 @@ impl<'b> Controller<'b> {
         let writer = output_type.handle()?;
         let mut no_errors: bool = true;
 
+        let stdin = io::stdin();
+
         for input_file in &self.config.files {
+            let mut reader = input_file.get_reader(&stdin)?;
+
             let result = if self.config.loop_through {
                 let mut printer = SimplePrinter::new();
-                self.print_file(&mut printer, writer, *input_file)
+                self.print_file(reader, &mut printer, writer, *input_file)
             } else {
-                let mut printer = InteractivePrinter::new(&self.config, &self.assets, *input_file);
-                self.print_file(&mut printer, writer, *input_file)
+                let mut printer =
+                    InteractivePrinter::new(&self.config, &self.assets, *input_file, &mut reader);
+                self.print_file(reader, &mut printer, writer, *input_file)
             };
 
             if let Err(error) = result {
@@ -43,13 +48,11 @@ impl<'b> Controller<'b> {
 
     fn print_file<'a, P: Printer>(
         &self,
+        reader: InputFileReader,
         printer: &mut P,
         writer: &mut Write,
         input_file: InputFile<'a>,
     ) -> Result<()> {
-        let stdin = io::stdin();
-        let reader = input_file.get_reader(&stdin)?;
-
         printer.print_header(writer, input_file)?;
         self.print_file_ranges(printer, writer, reader, &self.config.line_range)?;
         printer.print_footer(writer)?;
diff --git a/src/printer.rs b/src/printer.rs
index 527aff0b..98011fc3 100644
--- a/src/printer.rs
+++ b/src/printer.rs
@@ -15,7 +15,7 @@ use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineN
 use diff::get_git_diff;
 use diff::LineChanges;
 use errors::*;
-use inputfile::InputFile;
+use inputfile::{InputFile, InputFileReader};
 use preprocessor::expand;
 use style::OutputWrap;
 use terminal::{as_terminal_escaped, to_ansi_color};
@@ -74,7 +74,12 @@ pub struct InteractivePrinter<'a> {
 }
 
 impl<'a> InteractivePrinter<'a> {
-    pub fn new(config: &'a Config, assets: &'a HighlightingAssets, file: InputFile) -> Self {
+    pub fn new(
+        config: &'a Config,
+        assets: &'a HighlightingAssets,
+        file: InputFile,
+        reader: &mut InputFileReader,
+    ) -> Self {
         let theme = assets.get_theme(&config.theme);
 
         let colors = if config.colored_output {
@@ -124,7 +129,7 @@ impl<'a> InteractivePrinter<'a> {
         };
 
         // Determine the type of syntax for highlighting
-        let syntax = assets.get_syntax(config.language, file);
+        let syntax = assets.get_syntax(config.language, file, reader);
         let highlighter = HighlightLines::new(syntax, theme);
 
         InteractivePrinter {