aarondandy avatar aarondandy committed 1d0f0b0

work on html and xml features

Comments (0)

Files changed (14)

src/YouCantSpell.ReSharper.Shared/CSharp/CSharpFreeTextSpellingHighlightFinder.cs

 			ITreeNode node,
 			string text,
 			DocumentRange textRange
-		)
-		{
+		) {
 			var parser = new CStyleFreeTextParser();
 			var xmlTextParts = parser.ParseXmlTextParts(new TextSubString(text));
 			var wordParts = xmlTextParts.SelectMany(x => parser.ParseSentenceWordsForSpellCheck(x));
 				var word = wordPart.SubText;
 
 				// Make sure that the word is not to be ignored for any reason.
-				if(WordIsIgnored(word))
-					continue;
+				if(WordIsIgnored(word)) continue;
 
 				// Finally we check the spelling of the word.
-				if (_shellSpellCheckResources.Primary.Check(word))
-					continue;
+				if (_shellSpellCheckResources.Primary.Check(word)) continue;
 
 				// If we got this far we need to offer spelling suggestions.
 				var wordPartDocumentOffset = textRange.TextRange.StartOffset + wordPart.Offset;

src/YouCantSpell.ReSharper.Shared/Html/HtmlFreeTextSpellingHighlightFinder.cs

 using System;
 using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using JetBrains.DocumentModel;
+using JetBrains.ReSharper.Daemon;
+using JetBrains.Util;
+using YouCantSpell.CStyle;
+using JetBrains.ReSharper.Psi.Tree;
 
 namespace YouCantSpell.ReSharper.Html
 {
-	[Obsolete]
-	class HtmlFreeTextSpellingHighlightFinder
+	public class HtmlFreeTextSpellingHighlightFinder
 	{
+
+		private readonly ShellSpellCheckResources _shellSpellCheckResources;
+		private readonly IDocument _document;
+		private readonly int _minWordSize = 2;
+
+		public HtmlFreeTextSpellingHighlightFinder(
+			ShellSpellCheckResources shellSpellCheckResources,
+			IDocument document
+		) {
+			_shellSpellCheckResources = shellSpellCheckResources;
+			_document = document;
+		}
+
+		private bool WordIsIgnored(string word) {
+			return String.IsNullOrEmpty(word)
+				|| word.Length < _minWordSize
+				|| _shellSpellCheckResources.IsIgnoredInsensitive(word)
+				|| CStyleFreeTextParser.LooksLikeCodeWord(word);
+		}
+
+		public IEnumerable<HighlightingInfo> FindWordHighlightings(
+			ITreeNode node,
+			string text,
+			DocumentRange textRange
+		) {
+			var parser = new CStyleFreeTextParser();
+			var wordParts = parser.ParseSentenceWordsForSpellCheck(new TextSubString(text));
+			foreach(var wordPart in wordParts) {
+				var word = wordPart.SubText;
+
+				// Make sure that the word is not to be ignored for any reason.
+				if (WordIsIgnored(word)) continue;
+
+				// Finally we check the spelling of the word.
+				if (_shellSpellCheckResources.Primary.Check(word)) continue;
+
+				// If we got this far we need to offer spelling suggestions.
+				var wordPartDocumentOffset = textRange.TextRange.StartOffset + wordPart.Offset;
+				var wordRange = new DocumentRange(_document, new TextRange(wordPartDocumentOffset, wordPartDocumentOffset + word.Length));
+				var highlight = new HtmlSpellingErrorHighlighting(
+					node,
+					wordRange,
+					word,
+					null
+				);
+				yield return new HighlightingInfo(wordRange, highlight);
+			}
+		}
+
 	}
 }

src/YouCantSpell.ReSharper.Shared/Html/HtmlSpellCheckDaemonStageProcess.cs

 using JetBrains.ReSharper.Daemon;
 using JetBrains.ReSharper.Psi;
 using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
-using JetBrains.ReSharper.Psi.Html;
-using JetBrains.ReSharper.Psi.Html.Impl.Parsing;
 using JetBrains.ReSharper.Psi.Html.Impl.Tree;
 using JetBrains.ReSharper.Psi.Html.Tree;
 using JetBrains.ReSharper.Psi.Tree;
 		private readonly ShellSpellCheckResources _spellCheckResources;
 		private readonly IPsiSourceFile _sourceFile;
 		private readonly IHtmlFile _htmlFile;
-		private readonly int _minWordSize = 2;
+		private readonly HtmlFreeTextSpellingHighlightFinder _freeTextSpellingHighlightFinder;
 
-		public HtmlSpellCheckDaemonStageProcess(IDaemonProcess daemonProcess) {
+		public HtmlSpellCheckDaemonStageProcess(IDaemonProcess daemonProcess, IHtmlFile htmlFile) {
+			if (null == daemonProcess) throw new ArgumentNullException("daemonProcess");
+			if (null == htmlFile) throw new ArgumentNullException("htmlFile");
+
 			_daemonProcess = daemonProcess;
+			_htmlFile = htmlFile;
 			_spellCheckResources = Shell.Instance.GetComponent<ShellSpellCheckResources>();
 			_sourceFile = daemonProcess.SourceFile;
-#if RSHARP6
-			_htmlFile = DaemonProcess.SourceFile.GetPsiFile(HtmlLanguage.Instance) as IHtmlFile;
-#else
-			_htmlFile = DaemonProcess.SourceFile.GetPsiFiles<HtmlLanguage>().OfType<IHtmlFile>().Single();
-#endif
+			_freeTextSpellingHighlightFinder = new HtmlFreeTextSpellingHighlightFinder(
+				_spellCheckResources, _sourceFile.Document);
 		}
 
 		public IDaemonProcess DaemonProcess {
 			get { return _daemonProcess; }
 		}
 
-		public ShellSpellCheckResources SpellCheckResources {
-			get { return _spellCheckResources; }
-		}
-
-		public ISpellChecker PrimarySpellChecker {
-			get { return SpellCheckResources.Primary; }
-		}
-
-		private IDocument Document {
-			get { return _sourceFile.Document; }
-		}
-
 		private DocumentRange? FindTrueDocumentRange(TreeTextRange range) {
-			var validPartRanges = _htmlFile.GetIntersectingRanges(range)
-					.Where(x => x.Document == Document)
-					.ToList();
-			if(validPartRanges.Count != 1)
-				return null;
-			return validPartRanges[0];
+			return _htmlFile.GetIntersectingRanges(range)
+				.SingleOrDefault(x => x.Document == _sourceFile.Document);
 		}
 
 		private IEnumerable<HighlightingInfo> FindWordHighlightings(TreeElement node)
 		{
 			var validRange = FindTrueDocumentRange(node.GetTreeTextRange());
-			if(!validRange.HasValue)
-				yield break;
+			if (!validRange.HasValue) return Enumerable.Empty<HighlightingInfo>();
 
-			var textRange = validRange.Value;
+			return _freeTextSpellingHighlightFinder.FindWordHighlightings(node, node.GetText(), validRange.Value);
+		}
 
-			var wordText = node.GetText();
-			if(
-				null == wordText
-				|| String.IsNullOrEmpty(wordText)
-				|| wordText.Length < _minWordSize
-				|| SpellCheckResources.IsIgnoredInsensitive(wordText)
-				|| PrimarySpellChecker.Check(wordText)
-			) yield break;
+		private IEnumerable<HighlightingInfo> FindHighlightings(HtmlTag node) {
+			var results = new List<HighlightingInfo>(0);
+			foreach(var attribute in node.Attributes) {
+				var valueElement = attribute.ValueElement;
+				if(null == valueElement) continue;
 
-			var highlight = new HtmlSpellingErrorHighlighting(node, textRange, wordText, null);
-			yield return new HighlightingInfo(textRange, highlight);
+				var validRange = FindTrueDocumentRange(valueElement.GetUnquotedTreeTextRange());
+				if (!validRange.HasValue) continue;
+
+				results.AddRange(_freeTextSpellingHighlightFinder.FindWordHighlightings(
+					valueElement,
+					valueElement.UnquotedValue,
+					validRange.Value
+				));
+			}
+			return results;
 		}
 
 		public void Execute(Action<DaemonStageResult> commiter) {
-			if(_htmlFile == null)
-				return;
+			var textNodeTypes = _htmlFile.TokenTypes.TEXT;
+			var highlightings = new List<HighlightingInfo>(0);
 
-			var textNodeTypes = _htmlFile.TokenTypes.TEXT;
-			var highlightings = new List<HighlightingInfo>();
-
-			var allNodes = new List<ITreeNode>();
-			_htmlFile.ProcessChildren<ITreeNode>(allNodes.Add);
-
-			var allHtmlTags = new List<HtmlTag>();
-			_htmlFile.ProcessChildren<HtmlTag>(allHtmlTags.Add);
+			_htmlFile.ProcessChildren<HtmlTag>(node =>
+				highlightings.AddRange(FindHighlightings(node))
+			);
 
 			_htmlFile.ProcessChildren<TreeElement>(node => {
 				if(textNodeTypes.Equals(node.NodeType))

src/YouCantSpell.ReSharper.Shared/Html/HtmlSpellingFixBulbItem.cs

 using System;
-using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
 using JetBrains.ReSharper.Psi.Html.Parsing;
-using JetBrains.ReSharper.Psi.Html.Tree;
 using JetBrains.ReSharper.Psi.Tree;
 using YouCantSpell.Utility;
 

src/YouCantSpell.ReSharper.Shared/Html/HtmlSpellingSuggestionFormatter.cs

-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace YouCantSpell.ReSharper.Html
-{
-	class HtmlSpellingSuggestionFormatter
-	{
-	}
-}

src/YouCantSpell.ReSharper.Shared/Html/HtmlUtil.cs

 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 
 namespace YouCantSpell.ReSharper.Html
 {
+	[Obsolete]
 	class HtmlUtil
 	{
 	}

src/YouCantSpell.ReSharper.Shared/SpellCheckDaemonStage.cs

 using JetBrains.ReSharper.Psi;
 using JetBrains.ReSharper.Psi.CSharp;
 using JetBrains.ReSharper.Psi.Html;
+using JetBrains.ReSharper.Psi.Html.Tree;
+using JetBrains.ReSharper.Psi.Xml.Tree;
 using YouCantSpell.ReSharper.CSharp;
 using JetBrains.ReSharper.Psi.JavaScript.LanguageImpl;
 using YouCantSpell.ReSharper.Html;
 #endif
 		}
 
-		private bool HasAnyXml(IDaemonProcess process) {
+		private IXmlFile GetXmlFile(IPsiSourceFile sourceFile) {
 #if RSHARP6
-			return process.SourceFile.GetPsiFile(XmlLanguage.Instance) != null;
+			return sourceFile.GetPsiFile(XmlLanguage.Instance) as IXmlFile;
 #else
-			return process.SourceFile.GetPsiFiles<XmlLanguage>().Any();
+			return sourceFile.GetPsiFiles<XmlLanguage>().OfType<IXmlFile>().SingleOrDefault();
 #endif
 		}
 
-		private bool HasAnyHtml(IDaemonProcess process) {
+		private static IHtmlFile GetHtmlFile(IPsiSourceFile sourceFile) {
 #if RSHARP6
-			return process.SourceFile.GetPsiFile(HtmlLanguage.Instance) != null;
+			return sourceFile.GetPsiFile(HtmlLanguage.Instance) as IHtmlFile;
 #else
-			return process.SourceFile.GetPsiFiles<HtmlLanguage>().Any();
+			return sourceFile.GetPsiFiles<HtmlLanguage>().OfType<IHtmlFile>().SingleOrDefault();
 #endif
 		}
 
 		private List<IDaemonStageProcess> CreateProcessesCore(IDaemonProcess process, IContextBoundSettingsStore settings, DaemonProcessKind processKind) {
+			var sourceFile = process.SourceFile;
 			var stageProcesses = new List<IDaemonStageProcess>();
 			if (HasAnyCSharp(process))
 				stageProcesses.Add(new CSharpSpellCheckDaemonStageProcess(process, settings));
 			if (HasAnyJavaScript(process))
 				stageProcesses.Add(new JavaScriptSpellCheckDaemonStageProcess(process, settings));
-			//if(HasAnyXml(process))
-			//	stageProcesses.Add(new XmlSpellCheckDaemonStageProcess(process));
-			if(HasAnyHtml(process))
-				stageProcesses.Add(new HtmlSpellCheckDaemonStageProcess(process));
+
+			var xmlFile = GetXmlFile(sourceFile);
+			if(null != xmlFile)
+				stageProcesses.Add(new XmlSpellCheckDaemonStageProcess(process, xmlFile));
+
+			var htmlFile = GetHtmlFile(sourceFile);
+			if(null != htmlFile)
+				stageProcesses.Add(new HtmlSpellCheckDaemonStageProcess(process, htmlFile));
+			
 			return stageProcesses;
 		}
 

src/YouCantSpell.ReSharper.Shared/Xml/XmlFreeTextSpellingHighlightFinder.cs

 using System;
 using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using JetBrains.DocumentModel;
+using JetBrains.ReSharper.Daemon;
+using JetBrains.ReSharper.Psi.Tree;
+using JetBrains.Util;
+using YouCantSpell.CStyle;
 
 namespace YouCantSpell.ReSharper.Xml
 {
-	class XmlFreeTextSpellingHighlightFinder
+	public class XmlFreeTextSpellingHighlightFinder
 	{
+
+		private readonly ShellSpellCheckResources _shellSpellCheckResources;
+		private readonly IDocument _document;
+		private readonly int _minWordSize = 2;
+
+		public XmlFreeTextSpellingHighlightFinder(
+			ShellSpellCheckResources shellSpellCheckResources,
+			IDocument document
+		) {
+			_shellSpellCheckResources = shellSpellCheckResources;
+			_document = document;
+		}
+
+		private bool WordIsIgnored(string word) {
+			return String.IsNullOrEmpty(word)
+				|| word.Length < _minWordSize
+				|| _shellSpellCheckResources.IsIgnoredInsensitive(word)
+				|| CStyleFreeTextParser.LooksLikeCodeWord(word);
+		}
+
+		public IEnumerable<HighlightingInfo> FindWordHighlightings(
+			ITreeNode node,
+			string text,
+			DocumentRange textRange
+		) {
+			var parser = new CStyleFreeTextParser();
+			var wordParts = parser.ParseSentenceWordsForSpellCheck(new TextSubString(text));
+			foreach(var wordPart in wordParts) {
+				var word = wordPart.SubText;
+
+				// Make sure that the word is not to be ignored for any reason.
+				if (WordIsIgnored(word)) continue;
+
+				// Finally we check the spelling of the word.
+				if (_shellSpellCheckResources.Primary.Check(word)) continue;
+
+				// If we got this far we need to offer spelling suggestions.
+				var wordPartDocumentOffset = textRange.TextRange.StartOffset + wordPart.Offset;
+				var wordRange = new DocumentRange(_document, new TextRange(wordPartDocumentOffset, wordPartDocumentOffset + word.Length));
+				var highlight = new XmlSpellingErrorHighlighting(
+					node,
+					wordRange,
+					word,
+					null
+				);
+				yield return new HighlightingInfo(wordRange, highlight);
+			}
+		}
+
 	}
 }

src/YouCantSpell.ReSharper.Shared/Xml/XmlSpellCheckDaemonStageProcess.cs

 using System;
 using System.Collections.Generic;
 using System.Linq;
+using JetBrains.Application;
+using JetBrains.DocumentModel;
 using JetBrains.ReSharper.Daemon;
 using JetBrains.ReSharper.Psi;
 using JetBrains.ReSharper.Psi.Tree;
 using JetBrains.ReSharper.Psi.Xml;
+using JetBrains.ReSharper.Psi.Xml.Impl.Tree;
 using JetBrains.ReSharper.Psi.Xml.Tree;
 
 namespace YouCantSpell.ReSharper.Xml
 	{
 
 		private readonly IDaemonProcess _daemonProcess;
+		private readonly ShellSpellCheckResources _spellCheckResources;
+		private readonly IXmlFile _xmlFile;
+		private readonly IPsiSourceFile _sourceFile;
+		private readonly XmlFreeTextSpellingHighlightFinder _freeTextSpellingHighlightFinder;
 
-		public XmlSpellCheckDaemonStageProcess(IDaemonProcess daemonProcess) {
+		public XmlSpellCheckDaemonStageProcess(IDaemonProcess daemonProcess, IXmlFile xmlFile) {
+			if (null == daemonProcess) throw new ArgumentNullException("daemonProcess");
+			if (null == xmlFile) throw new ArgumentNullException("xmlFile");
+
 			_daemonProcess = daemonProcess;
+			_xmlFile = xmlFile;
+			_spellCheckResources = Shell.Instance.GetComponent<ShellSpellCheckResources>();
+			_sourceFile = daemonProcess.SourceFile;
+			_freeTextSpellingHighlightFinder = new XmlFreeTextSpellingHighlightFinder(
+				_spellCheckResources, _sourceFile.Document);
 		}
 
 		public IDaemonProcess DaemonProcess {
 			get { return _daemonProcess; }
 		}
 
-		private IXmlFile GetPsiFile() {
-#if RSHARP6
-			return DaemonProcess.SourceFile.GetPsiFile(XmlLanguage.Instance) as IXmlFile;
-#else
-			return DaemonProcess.SourceFile.GetPsiFiles<XmlLanguage>().OfType<IXmlFile>().Single();
-#endif
+		private DocumentRange? FindTrueDocumentRange(TreeTextRange range) {
+			return _xmlFile.GetIntersectingRanges(range)
+				.SingleOrDefault(x => x.Document == _sourceFile.Document);
+		}
+
+		private IEnumerable<HighlightingInfo> FindWordHighlightings(IXmlFloatingTextTokenNode node) {
+			var validRange = FindTrueDocumentRange(node.GetTreeTextRange());
+			if (!validRange.HasValue) return Enumerable.Empty<HighlightingInfo>();
+
+			return _freeTextSpellingHighlightFinder.FindWordHighlightings(node, node.GetText(), validRange.Value);
+		}
+
+		private IEnumerable<HighlightingInfo> FindWordHighlightings(XmlValueToken node) {
+			var validRange = FindTrueDocumentRange(node.GetTreeTextRange());
+			if (!validRange.HasValue) return Enumerable.Empty<HighlightingInfo>();
+
+			return _freeTextSpellingHighlightFinder.FindWordHighlightings(node, node.UnquotedValue, validRange.Value);
 		}
 
 		public void Execute(Action<DaemonStageResult> commiter) {
-			var file = GetPsiFile();
-			if (file == null)
-				return;
+			var highlightings = new List<HighlightingInfo>(0);
 
 			var allNodes = new List<ITreeNode>();
-			file.ProcessChildren<ITreeNode>(allNodes.Add);
+			_xmlFile.ProcessChildren<ITreeNode>(allNodes.Add);
 
-			"".ToCharArray();
+			_xmlFile.ProcessChildren<XmlFloatingTextToken>(node => {
+				if(!node.IsIdentifier())
+					highlightings.AddRange(FindWordHighlightings(node));
+			});
+
+			_xmlFile.ProcessChildren<XmlValueToken>(node => {
+				highlightings.AddRange(FindWordHighlightings(node));
+			});
+
+			commiter(new DaemonStageResult(highlightings));
 		}
+
+		
+
 	}
 }

src/YouCantSpell.ReSharper.Shared/Xml/XmlSpellingErrorHighlighting.cs

 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using JetBrains.DocumentModel;
+using JetBrains.ReSharper.Daemon;
+using JetBrains.ReSharper.Psi.Tree;
+using JetBrains.ReSharper.Psi.Xml;
 
 namespace YouCantSpell.ReSharper.Xml
 {
-	class XmlSpellingErrorHighlighting
+	[ConfigurableSeverityHighlighting(SeverityId, XmlLanguage.Name, OverlapResolve = OverlapResolveKind.NONE)]
+	public class XmlSpellingErrorHighlighting : SpellingErrorHighlightingBase
 	{
+
+		public XmlSpellingErrorHighlighting(ITreeNode node, DocumentRange errorRange, string wordInError, Func<string, string[]> getSuggestions)
+			: base(node, errorRange, wordInError, getSuggestions) { }
+
 	}
 }

src/YouCantSpell.ReSharper.Shared/Xml/XmlSpellingFixBulbItem.cs

 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using JetBrains.ReSharper.Psi.Tree;
+using YouCantSpell.Utility;
 
 namespace YouCantSpell.ReSharper.Xml
 {
-	class XmlSpellingFixBulbItem
+	public class XmlSpellingFixBulbItem : SpellingFixBulbItemBase<XmlSpellingErrorHighlighting>
 	{
+
+		public XmlSpellingFixBulbItem(XmlSpellingErrorHighlighting highlighting, string suggestion)
+			: base(highlighting, suggestion) { }
+
+		protected override Action<JetBrains.TextControl.ITextControl> ExecutePsiTransaction(JetBrains.ProjectModel.ISolution solution, JetBrains.Application.Progress.IProgressIndicator progress) {
+			if (!Highlighting.IsValid())
+				return null;
+
+			var node = Highlighting.Node;
+			var badWordTextRange = Highlighting.Range.TextRange;
+
+			var newText = StringUtil.ReplaceSection(
+				node.GetText(),
+				Suggestion,
+				badWordTextRange.StartOffset - node.GetDocumentRange().TextRange.StartOffset,
+				badWordTextRange.Length
+			);
+
+			
+			return null;
+		}
+
 	}
 }

src/YouCantSpell.ReSharper.Shared/Xml/XmlSpellingQuickFix.cs

-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using JetBrains.Annotations;
+using JetBrains.ReSharper.Feature.Services.Bulbs;
 
 namespace YouCantSpell.ReSharper.Xml
 {
-	class XmlSpellingQuickFix
+	[QuickFix]
+	public class XmlSpellingQuickFix : SpellingQuickFixBase<XmlSpellingErrorHighlighting, XmlSpellingFixBulbItem>
 	{
+
+		public XmlSpellingQuickFix([NotNull] XmlSpellingErrorHighlighting highlighting)
+			: base(highlighting) { }
+
+		protected override XmlSpellingFixBulbItem CreateSpellingFix(string suggestion) {
+			return new XmlSpellingFixBulbItem(Highlighting, suggestion);
+		}
 	}
 }

src/YouCantSpell.ReSharper.v61/YouCantSpell.ReSharper.v61.csproj

     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlSpellingQuickFix.cs">
       <Link>Html\HtmlSpellingQuickFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlSpellingSuggestionFormatter.cs">
-      <Link>Html\HtmlSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlUtil.cs">
       <Link>Html\HtmlUtil.cs</Link>
     </Compile>

src/YouCantSpell.ReSharper.v70/YouCantSpell.ReSharper.v70.csproj

     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlSpellingQuickFix.cs">
       <Link>Html\HtmlSpellingQuickFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlSpellingSuggestionFormatter.cs">
-      <Link>Html\HtmlSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlUtil.cs">
       <Link>Html\HtmlUtil.cs</Link>
     </Compile>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.